What is GDI+ ?

If you have programmed under Windows you are familiar with the term GDI (Graphical Device Interface). GDI simplifies drawing by providing an interface to the hardware devices like screen or printer such that the programmers don’t need to bother about hardware details and their differences. The same program can work on different display adapters, printers, keyboards, etc. without modifying it.

.NET uses GDI+, an extension to GDI, which further simplifies drawing. GDI+ has added several new features like graphics paths, support to image file formats, image transformation, etc. GDI+ has also modified the programming model by introducing fundamental changes in the programming model used by GDI.

Changes In Programming Model

GDI uses an idea of Device Context (DC). Device Context is a structure that stores all the drawing related information viz. features of display device and attributes that decide the appearance of the drawing. Every device context is associated with a window. To draw on a window, one must first obtain a device context of that window. If we want to change any attribute, say, pen color we first select it in the device context by calling the SelectObject( ) method. Once selected all the drawing is done using this pen unless and until we select another pen in device context.

GDI+ works with ‘graphics context’ that plays similar role as device context. The graphics context is also associated with a particular window and contains information specifying how drawing would be displayed. However, unlike device context it does not contain information about pen, brush, font, etc. If we want to draw with new pen we simply have to pass an object of Pen class to the DrawLine( ) method (this method draws line on window). We can pass different Pen objects in each call to DrawLine( ) method to draw the lines in different colors. Thus GDI uses a stateful model, whereas, GDI+ uses a stateless model. The Graphics class encapsulates the graphics context. Not surprisingly, most of the drawing is done by calling methods of the Graphics class.

Working With GDI+

We would see how to draw text and graphics using GDI+ by writing a small program. Create a Windows Application. Windows programmers know that a window receives WM_PAINT message when it is to be painted. We need to handle this message if we want to do any painting in the window. In .NET to do this we can either override the virtual method OnPaint( ) of the Form class or write a handler for the Paint event. The base class implementation of OnPaint( ) invokes the Paint event handler through delegate. Hence we should write our code in the Paint event handler.

Add the Paint handler to the form. The Form1_Paint( ) handler would look like this.

private void Form1_Paint ( object sender, PaintEventArgs e )
{
}

The first parameter passed to the Form1_Paint( ) handler contains the reference to the object of a control that sends the event. The second parameter contains more information about the Paint event. We would first see how to display a string on the form. To display the string we would use DrawString( ) method of the Graphics class.

private void Form1_Paint ( object sender, PaintEventArgs e )
{

Graphics g = e.Graphics ;
Font myfont = new Font ( "Times New Roman", 60 ) ;
StringFormat f = new StringFormat( ) ;
f.Alignment = StringAlignment.Center ;
f.LineAlignment = StringAlignment.Center ;
g.DrawString ( "Hello!", myfont, Brushes.Blue, ClientRectangle, f ) ;

}

The Graphics property of the PaintEventArgs class contains reference to the Graphics object. We can use this reference for drawing. In a handler other than Paint event handler we can obtain the Graphics reference using the CreateGraphics( ) method of the Form class. The DrawString( ) method has several overloaded versions. We used one that allows us to display centrally aligned text in desired font and color. The first parameter passed to the DrawString( ) method is the string we wish to display. The second parameter is the font in which text would get displayed. We have created a font by passing the font name and font size to the constructor of the Font class. The text gets filled with the brush color specified as the third parameter. The fourth parameter specifies the surrounding rectangle. We have passed ClientRectangle property of Form class that contains a rectangle representing the client area of the form. To centrally align the text we have used the StringFormat class. The Alignment and LineAlignment properties of this class contain horizontal and vertical alignment of text respectively.

The Graphics class contains various methods to draw different shapes. This includes drawing rectangle, line, arc, bezier, curve, pie, etc. We would add the code in Form1_Paint( ) handler that draws rectangles in different pens and brushes. You would be able to draw other shapes on similar lines.

The following code draws a rectangle using green colored pen having line thickness of 3.

Pen p = new Pen ( Color.Green, 3 ) ;
g.DrawRectangle ( p, 20, 20, 150, 100 ) ;

The Pen class encapsulates various styles of pens like solid, dash, dash-dot, etc. We can change the style of pen using the DashStyle property of the Pen class. This is shown in the following statement.

p.DashStyle = DashStyle.Dash ;

If we want, we can specify custom pen style by using the DashPattern property. There are several other properties of the Pen class that allow us to specify the pen type (hatch fill, gradient fill, solid color, etc), cap style, join style, etc.

Unlike GDI, GDI+ provides separate methods for rectangle and filled rectangle. To fill the rectangle we need to pass a Brush object. This is shown below.

HatchBrush hb = new HatchBrush ( HatchStyle.BackwardDiagonal,Color.Red, Color.Black ) ;
g.FillRectangle ( hb, 200, 20, 150, 100 ) ;

We have used hatch brush to fill the rectangle. The hatch brush is created using the HatchBrush class. We have mentioned the hatch style as BackwardDiagonal. The rectangle will get filled with the hatch brush in red and black color combination. Like the HatchBrush class there are several other classes used to fill the shapes with viz. SolidBrush, TextureBrush, and LinearGradientBrush. Gradient brush is something that was not available in GDI. Let us see how to use it.

LinearGradientBrush gb = new LinearGradientBrush ( ClientRectangle,
Color.BlanchedAlmond, Color.Aquamarine, 90 ) ;
g.FillRectangle ( gb, ClientRectangle ) ;

Here, we have created an object of the LinearGradientBrush class and passed to its constructor the rectangle to be filled, and two colors that form the gradient pattern. The last parameter specifies the angle from which we wish to draw. Specifying 90 would fill the window vertically.

Coordinates And Transformations

In the Graphics methods we specify coordinates in two-dimensional coordinate system. The system has origin at left-top corner and x and y axes point to right and down respectively. All the methods take coordinates in pixels. The coordinates passed to Graphics methods are world coordinates. When we pass world coordinates to a method, they firstly get translated into page coordinates (logical coordinates) and then into device coordinates (physical coordinates). Ultimately, the shape gets drawn in device coordinates. In both the page and device coordinate system the measure of unit is same i.e pixels. We can customize the coordinate system by shifting the origin to some other place in client area and by setting a different measure of unit. Let us see how this can be achieved. We would first draw a horizontal line having 1 inch of width. Here is the code to do this.

private void Form1_Paint ( object sender, PaintEventArgs e )
{

Graphics g = e.Graphics ;
g.PageUnit = GraphicsUnit.Inch ;
Pen p = new Pen ( Color.Green, 1 / g.DpiX ) ;
g.DrawLine ( p, 0, 0, 1, 0 ) ;

}

Here firstly we have set the PageUnit property to GraphicsUnit.Inch specifying that the unit of measure is an inch. We have created a Pen object and set its width to 1 / g.Dpix. The Dpix property of the Graphics class indicates a value, in dots per inch, for the horizontal resolution supported by this Graphics object. Note that this is necessary because now Pen object also assumes 1 unit = 1 inch. So, if we don’t set the pen width like this, a line with 1 inch pen width would get drawn. Next we drew a line having one unit measure, which happens to be an inch.

Let us now shift the origin to the center of the client area and draw the line again.

private void Form1_Paint ( object sender, PaintEventArgs e )
{

Graphics g = e.Graphics ;
g.PageUnit = GraphicsUnit.Inch ;
g.TranslateTransform ( ( ClientRectangle.Width / g.DpiX ) / 2,
( ClientRectangle.Height / g.DpiY ) / 2 ) ;
Pen p = new Pen ( Color.Green, 1 / g.DpiX ) ;
g.DrawLine ( p, 0, 0, 1, 0 ) ;

}

Here, after setting the unit to an inch using the PageUnit property, we have called the TranslateTransform( ) method to shift the origin to the center of the client area. This method maps the world coordinates to page coordinates and so the transformation is called world transformation. The x and y values we have passed to the TranslateTransform( ) method get added to every x and y values we pass to the Graphics methods. Finally, we have created a pen having proper width and drew the line.

GDI+ also allows us to orient the x and y axes’s direction to the specified angle. For this, it provides the RotateTransform( ) method. For example, if we call the RotateTransform( ) method before drawing the line as shown below,

g.RotateTransform ( 30 ) ;

then line would get displayed slanting downwards, 30 degrees below the base line. We can use this functionality of the RotateTransform( ) method to create an application like analog clock.

Disposing Graphics Objects

Whenever we open a file, we close it after we have finished working with the file. This is because a handle is associated with the file that remains open if we don’t close it explicitly. Similarly, GDI+ resources like pens, brushes, fonts need to be disposed of because they encapsulate GDI+ handles in them. To release the GDI+ resources, we can call the Dispose( ) method on every object that is to be released. For example, following statement would release the pen object represented by penobject using the Dispose( ) method.

penobject.Dispose( ) ;

We must also release the Graphics object obtained by calling the CreateGraphics( ) method.

Posted bySumedh at 10:23 PM 0 comments  

Antialiasing in GDI+

Look at the difference between the following texts:

A careful observation will make us aware that the first string has a jagged effect and the second is smooth. (The effect will be more visible in the actual output). This is because we used antialiasing in the second one. The screen is divided into small pixels. When we try to draw curved objects on the screen they don�t perfectly map to a grid of pixels. This results in a jagged effect or �jaggies�. To remove this we use a technique called antialiasing Following is the code for this program:

protected void Form1_Paint ( object sender, System.WinForms.PaintEventArgs e )
{

Graphics g = e.Graphics ;
SolidBrush mybrush = new SolidBrush ( Color.Blue ) ;
Font myfont = new Font ( "Times new Roman", 25,
FontStyle.BoldItalic ) ;
Matrix mymat = new Matrix ( ) ;

mymat.Scale ( 5, 5 ) ;
g.Transform = mymat ;

g.DrawString ( "Cool", myfont, mybrush, 0, 0 ) ;
g.TextRenderingHint = TextRenderingHint.AntiAlias;
g.DrawString ( "Cool", myfont, mybrush, 0, 30 ) ;

}


The TextRenderingHint is an enumeration which specifies the quality of text rendering. We equate the AntiAlias member of this enumeration to the TextRenderingHint property of the Graphics class.

Posted bySumedh at 9:32 PM 0 comments  

Trasformations in GDI+

Transforming an object includes Translation, Rotation, Scaling and Shearing. We plan to apply these transformations to our text. We plan to do something like this

In this program we have drawn the shadow first and then the text, so that the text overlaps the shadow making the shadow appear as if it is behind the text. We have drawn the shadow using the same font as the original text and using gray color. The shadow should be half the size of the original text and must be sheared in the x direction.

The code for this program is

protected void Form1_Paint (object sender, System.WinForms.PaintEventArgs e)
{

Graphics g = e.Graphics ;
Font myfont = new Font ( "Times New Roman", 100 ) ;

Matrix mymat = new Matrix ( );
mymat.Shear ( -1.4f, 0f ) ;
mymat.Scale ( 1, 0.5f ) ;
mymat.Translate ( 236, 170 ) ;

g.Transform = mymat ;

SolidBrush mybrush = new SolidBrush ( Color.Gray ) ;
g.DrawString ( "K", myfont, mybrush, 50, 50 ) ;
g.DrawString ( "I", myfont, mybrush, 150, 50 ) ;
g.DrawString ( "C", myfont, mybrush, 200, 50 ) ;
g.DrawString ( "I", myfont, mybrush, 300, 50 ) ;
g.DrawString ( "T", myfont, mybrush, 350, 50 ) ;
g.ResetTransform ( ) ;

mybrush.Color = Color.DarkMagenta ;
g.DrawString ( "K", myfont, mybrush, 50, 50 ) ;

mybrush.Color = Color.FromArgb ( 150, 0, 255, 255 ) ;
g.DrawString ( "I", myfont, mybrush, 150, 50 ) ;

LinearGradientBrush lgb = new LinearGradientBrush ( new
Point ( 200, 50 ), new Point ( 350, 200 ), Color.Brown,
Color.Yellow ) ;
g.DrawString ( "C", myfont, lgb, 200, 50 ) ;

HatchBrush hb = new HatchBrush(
HatchStyle.DiagonalCross, Color.Blue, Color.Red ) ;
g.DrawString ( "I", myfont, hb, 300, 50 ) ;

Image myimg = Image.FromFile ( @"C:\test.bmp" );
TextureBrush tb = new TextureBrush ( myimg ) ;
g.DrawString ( "T", myfont, tb, 350, 50 ) ;

}

We have created an object of the Matrix class. Transformations are always applied using Matrices. Matrix addition and multiplication result in various transformations.

We have applied shearing by �1.4 in x direction using the Shear( ) method. Next we have scaled the matrix by half, using the Scale( ) method. Passing a 0.5 as the second coordinate results in reduction in the y direction by half. After doing all this, the coordinate column of the matrix also gets multiplied by some factor resulting in some different coordinates. These coordinates have to be brought to their actual positions. We did this using the Translate( ) method.

After applying all the transformations to the Matrix, we have set the Transform property of the Graphics class to this matrix. The Transform property of the Graphics class sets or gets the world transform for the Graphics object.

Under such a transformed scenario we have drawn the strings at the specified positions using the specified Brush and Font. Using the DrawString( ) method. This will fully create the shadow part.

The original text drawing is simple; we must get rid of the transformations applied. This is done by resetting the Transformation world by using the ResetTransform( ) method.

To draw a �K� we have used a solid brush with a DarkMegenta Color.To draw the �I� we have used a Transparent Brush. A Brush can be made Transparent by setting the alpha component of its color to a value less than 255 using the FromArgb( ) method.. The alpha value ranges from 0 (fully transparent) to 255(fully opaque). The default is opaque.We have drawn �C� with a GradientBrush, while �I� with a HatchBrush. To draw the �T�, we have used an image in a TextureBrush.

Posted bySumedh at 9:32 PM 0 comments  

Mirror Images using Transformation

In the following program we plan to draw a mirror image of an existing image

namespace immageprocessing
{


using System ;
using System.Drawing ;
using System.Drawing.Text ;
using System.Drawing.Drawing2D ;
using System.Collections ;
using System.ComponentModel ;
using System.WinForms ;
using System.Data ;

public class Form1 : System.WinForms.Form
{


private System.ComponentModel.Container components ;
public Form1 ( )
{

InitializeComponent ( ) ;

}

public override void Dispose ( )
{

base.Dispose ( ) ;
components.Dispose ( ) ;

}

private void InitializeComponent ( )
{

this.components = new
System.ComponentModel.Container ( ) ;
this.Text = "Form1" ;
this.AutoScaleBaseSize = new System.Drawing.Size ( 5,13 ) ;
this.ClientSize = new System.Drawing.Size ( 792, 549 ) ;
this.Paint += new System.WinForms.PaintEventHandler ( this.Form1_Paint ) ;
}

protected void Form1_Paint ( object sender, System.WinForms.PaintEventArgs e )
{

Graphics g = e.Graphics ;
Image myimg = Image.FromFile ( @"C:\fcode.jpg" ) ;
Matrix mymat = new Matrix ( ) ;
mymat.Translate ( 50, 50 ) ;
mymat.Scale ( -1.0f, 1 ) ;
mymat.Translate ( -450, -50 ) ;
g.Transform = mymat ;
g.DrawImage ( myimg, 50, 50 ) ;
g.ResetTransform ( ) ;

}

public static void Main(string[] args)
{

Application.Run(new Form1());

}

}

}

Posted bySumedh at 9:31 PM 0 comments  

DrawString( ) Demo

The main work of displaying text is done by the DrawString( ) method. The DrawString( ) method has many overloads but what we have used here is this

public void DrawString(string, Font, Brush, float, float);

The first parameter represents the string to be draw. Second and third parameters specify the Font and Brush to draw with. The last two parameters represent the x and y coordinates of the desired point at which string will be drawn.

In this program we should be able to display text wherever we click with the mouse in the Form. Moreover the text should be of random Font, Color and Size.

We have to add the MouseDown event. The code to add in the handler looks like this:

protected void Form1_MouseDown (object sender, System.WinForms.MouseEventArgs e)
{

Graphics g = CreateGraphics( );
Random r = new Random ( );
int x, y ;
FontFamily[] f = FontFamily.Families ;
int i = r.Next ( f.GetUpperBound ( 0 ) ) ;

x = e.X ;
y = e.Y ;

Font myfont = new Font ( f[i] ,r.Next (14, 40 ) ) ;
Color c1 = Color.FromArgb ( r.Next(255), r.Next(255), r.Next(255));
SolidBrush mybrush = new SolidBrush ( c1 );
g.DrawString ( "Hello", myfont, mybrush, x,y );

}

As we want some attributes to be random we have used an object of the Random class. The Random class represents a random number generator. A mathematical function is used here to generate random numbers. The Next( ) method of this class returns a random number. If we pass an integer to this function it returns a positive random number less than the specified maximum range.

The Families property of the FontFamily class returns an array that contains all of the FontFamily objects associated with the current graphics context.

We have collected such a collection of font families in array f. To the Next( ) method we passed the upper bound of the array whose return value will be used as an index.

In x and y we store the x and y coordinates of the point where the mouse was pressed down.

To create a random Font object we have passed the randomly generated index. This will pick any one of the font families from the array. The Color of the Brush is also generated randomly every time. If we pass 255 as a parameter to the Next( ) method, it avoids generation of an invalid red, blue and green component. Now we can display the desired text at the x and y coordinates.

The output is :

Posted bySumedh at 9:31 PM 0 comments  

Pens in GDI+

Consider the following code:

protected void Form1_Paint (object sender, System.WinForms.PaintEventArgs e)
{

this.BackColor = Color.White ;
Graphics g = e.Graphics;

Pen p = new Pen ( Color.FromArgb ( 0 ,255, 0 ) ,3) ;
g.DrawRectangle ( p, 20, 20, 150, 100 ) ;

p.DashStyle = DashStyle.Dash ;
g.DrawRectangle ( p, 20, 150, 150, 100 ) ;

HatchBrush hb = new HatchBrush ( HatchStyle.BackwardDiagonal,
Color.Red, Color.Black ) ;
p.Brush = hb ;
g.DrawRectangle ( p, 200, 20, 150, 100 ) ;

p.SetLineCap ( LineCap.ArrowAnchor, LineCap.ArrowAnchor,
DashCap.Triangle ) ;
g.DrawRectangle ( p, 200, 150, 150 ,100 ) ;
g.DrawRectangle ( p, 200, 150, 150 ,100 ) ;

}

We can change the Background color of the Form by setting the BackColor property to some other color. We have created a Red Pen with Width 3.

We can change the style of Pen by setting its DashStyle property. The DashStyle Enumeration specifies the style of dashed lines drawn with a Pen. Here we have simply used the Dash member.

We can change the brush that determines the attributes of the Pen by setting the Brush property of the Pen to HatchBrush.

The SetLineCap( ) sets the values that determine the style of cap used to end lines drawn by the Pen. The first two parameters passed represent the startCap and endCap that represent the cap style to use at the beginning and ending of lines drawn by the Pen. The last parameter represents the cap style to use at the beginning or end of dashed lines drawn with this Pen. So we have used only this parameter. LineCap is an enumeration specifying the cap style for the Pen. The output of this program is:

Now suppose we want to fill the entire Form with an image, our code would simply be

protected void Form1_Paint (object sender, System.WinForms.PaintEventArgs e)
{

Graphics g = e.Graphics ;
Size sz = this.ClientSize ;
Image img = Image.FromFile ( @"C:\fcode.jpg") ;
TextureBrush t = new TextureBrush ( img ) ;
for ( int h = 0 ; h <= sz.Height ;h += img.Height )
for ( int w = 0 ; w <= sz.Width ; w += img.Width )
g.FillRectangle( t, w, h, sz.Width, sz.Height ) ;

}

The ClientSize property gets or sets the size of the client area of the form. The Size structure represents the size of a rectangular region with an ordered pair of width and height. Rest is very obvious.

Posted bySumedh at 9:30 PM 0 comments  

Working with Brushes

We have used four brushes SolidBrush, HatchBrush, TextureBrush, and LinearGradientBrush in the following program. Here is how the Paint event will be.

protected void Form1_Paint (object sender, System.WinForms.PaintEventArgs e)
{

Graphics g = e.Graphics;
Pen p = new Pen ( Color.Black, 4 ) ;

SolidBrush mysb = new SolidBrush(Color.FromArgb ( 0 ,255, 0 ));
g.FillRectangle ( mysb, 20, 20, 100, 200 ) ;
g.DrawRectangle ( p, 20, 20, 100, 200 ) ;

HatchBrush myhb = new HatchBrush (
HatchStyle.DiagonalCross,Color.Black,Color.Red );
g.FillRectangle ( myhb, 160, 20, 100, 200 ) ;
g.DrawRectangle ( p, 160, 20, 100, 200 ) ;

Image myimg = Image.FromFile( @"C:\fcode.jpg") ;
TextureBrush mytb = new TextureBrush ( myimg ) ;
g.FillRectangle ( mytb, 300, 20, 100, 200 ) ;
g.DrawRectangle ( p, 300, 20, 100, 200 ) ;

LinearGradientBrush mylgb = new LinearGradientBrush ( new
Point(440,20), new Point(540, 220),Color.Yellow,Color.Brown );
g.FillRectangle ( mylgb, 440, 20, 100, 200 ) ;
g.DrawRectangle ( p, 440, 20, 100, 200 ) ;

}

For using these brushes we need to add the Following code

using System.Drawing.Drawing2D ;

These brushes are defined in this namespace. Method for SolidBrush is the same.

For the HatchBrush we have specified the HatchStyle along with the Color. HatchStyle is an enum specifying different patterns available for HatchBrush objects

In the constructor of the TextureBrush class we need to pass an image. The Image object is created with the help of the FromFile( ) method. Using such a TextureBrush we can fill the rectangle.

To the constructor of a LinearGradientBrush we have passed two Points and two Colors. The points specify the starting and ending point of the Gradient, while the Colors represent the starting and ending Colors of the Gradient. The output looks like this:

Posted bySumedh at 9:30 PM 0 comments