Category: GDI+


In the last part I gave you a bird’s eye view of what GDI+ is and how to setup application for using GDI+. However, without knowing about the facilitator i.e. GDI+, going forward would be counter-productive. Hence, in this part, along with different image manipulation techniques, the focus would be on the essentials of GDI+. In the first section, I would discuss about GDI+ in brief including the differences it has with its predecessor that is GDI. Second section would focus on the manipulation techniques such as zooming, creating thumbnails. In the third section, I would extend the application created in last part to include the techniques introduced in this discussion. That is the outline of this discussion.

More About GDI+:

GDI+ essentially refers to the library that helps developers to interact with various devices such as Monitors, Printers etc. having graphical capabilities without going into low-level details of these devices. The essence of GUI is that it can interact with peripherals such as Monitors and presents data in human readable form. However, from the point of view of a developer, if he or she had to directly interact with these devices, then such a task would have been monumental. This is where GDI+ comes into picture. It acts as a conduit and a translator for the data being passed between devices and applications. Even controlling the command line terminals also comes under GDI+. It does everything from printing a ‘Hello World’ program on the console to drawing lines, rectangles etc and printing a form. Pictorially it can be shown thus:

Fig 1- GDI2_1.jpg

The next question that arises is how does GDI+ works? To make it clear as a crystal, lets have a look at an example of drawing a line. A line, in essence, is a sequence of pixels from starting location (X­­0, Y0) to an ending location (Xn, Yn). To draw such a line the devices i.e. monitor in this case, need to know the device coordinate or physical coordinates. However, instead of directly telling the device, the call is made to the drawLine() method of GDI+, and GDI+ draws the line from point A to point B in the memory also known as video memory. GDI+ reads the point A and point B locations, converts them to a sequence of pixels, and tells the monitor to display the sequence of pixels. In short GDI+ converts device independent calls to device understandable form and vice-versa. So that’s an overview of how GDI+ works. Lets now move onto topic of manipulation of image.

Image Manipulation – Thumbnail, zooming and saving:

In the last part, I discussed simple manipulations such as flipping and rotating. Lets tackle something a bit more complex in concept if not in implementation. They are:

1. Creating thumbnails

2. Zooming a loaded image

3. Saving a manipulated image

Of these first two strictly comes under the category of image manipulation whereas the last is a generic image based operation in the lines of file operation.

1. Creating Thumbnails:

Thumbnails are reduced version of images. The dimensions of a thumbnail image are typically of 80 to 200 pixels in length. In GDI+, thumbnail of an image can be created by GetThumbnailImage() of Image class. It takes four parameters- the first parameter corresponds to the width and second parameter is for the height of the thumbnail to be generated, the third parameter is Image.GetThumbnailImageAbort which needs to be passed for compatibility though not being used in current version. The fourth parameter, likewise is not used, yet need to be passed for compatibility. The fourth parameter must be IntPtr.Zero. If the first two parameters that is width and height are 0, then GDI+ returns embedded thumbnail (some images contains thumbnails embedded into them. Otherwise the thumbnail is created using system defined dimensions. For example if img is an instance of Image class and the width, height to be used is system defined, the statement to create a thumbnail would be:

Image thumbNailImage = img.GetThumbnailImage(0, 0, tnCallBack, IntPtr.Zero);

Where thumbNailImage contains the returned thumbnail, the tnCallback is a function corresponding to Image.GetThumbnailImageAbort which is defined as:

// Must be called, but not used

public bool tnCallbackMethod()

{

return false;

}

2. Zooming a loaded image:

Zooming is the process of enlarging an image by multiplying it with a number called zoom-factor. By definition, “The zoom factor is the ratio of the current size of the image to the desired new size of the image”. That means by dividing the current size of the image with desired size of the image we get the zoom factor. For example, to zoom in on an image by 200%, the current size must be multiplied by 200% or by 2 (200%= 200/100=2). To zoom out an image by 25 percent, the size must be multiplied by 25 percent, or 0.25 (25/100 = 0.25 times). It is one of those areas of image manipulation where GDI+ doesn’t provide methods to achieve the result.

3. Saving a manipulated image:

Saving an image is one of the primary operations done on an image. While saving an image, the type into which the image has to be saved or in other words the extension of the image plays a major role. Each type corresponds to a particular format. In essence, while saving an image writing out the data according to the format is necessary. However, since the API that is being used is GDI+, a single call to Save() method of Image class abstracts out all the details about writing out according to the format. It takes two parameters – the name as which the image is to be saved and the format into which the image has to be saved. The format can be specified using the types provided by ImageFormat class. Following table specifies the various formats supported by GDI+:

Property

Description

Bmp Specifies BMP format.
Emf Specifies EMF (Enhanced Metafile Format).
Exif Specifies EXIF format.
Gif Specifies GIF format.
Guid Specifies a GUID structure that represents the ImageFormat object.
Icon Specifies Windows icon format.
Jpeg Specifies JPEG format.
MemoryBmp Specifies memory bitmap format.
Png Specifies PNG format.
Tiff Specifies TIFF format.
Wmf Specifies WMF (Windows Metafile Format

Of these Emf and Wmf are specific to windows. I will be discussing about these, in detail, in future.

For example to save the image with the name “checker.gif” in GIF format, the statement would be:

curImage.Save(“checker.gif”, ImageFormat.Gif);

where curImage is the instance of Image class.

That brings us to the end of this section. In the next section, I would be extending the application developed in the first part by embedding the operations discussed in this section.

Image Manipulation – In the Real World:

It is time to put the theory into practice. I would be doing it by enhancing the application developed in the last part by providing the following functionalities:

1. Save the image in the format specified by user

2. Zoom-in according to the percentage selected from the menu

3. Create thumbnail of a loaded image

The menus corresponding to the operations are:

mnuSave – the submenu of File menu to save the image

mnu200Zoom – zooms the image by 200%.

mnuThumbNail- creates a thumbnail of the image.

Here is the method that handles click event of mnuSave

private void mnuSave_Click(object sender,

System.EventArgs e)

{

// If image is created

if(curImage == null)

return;

// Call SaveFileDialog

SaveFileDialog saveDlg = new SaveFileDialog();

saveDlg.Title = “Save Image As”;

saveDlg.OverwritePrompt = true;

saveDlg.CheckPathExists = true;

saveDlg.Filter =

“Bitmap File(*.bmp)|*.bmp|” +

“Gif File(*.gif)|*.gif|” +

“JPEG File(*.jpg)|*.jpg|” +

“PNG File(*.png)|*.png” ;

saveDlg.ShowHelp = true;

// If selected, save

if(saveDlg.ShowDialog() == DialogResult.OK)

{

// Get the user-selected file name

string fileName = saveDlg.FileName;

// Get the extension

string strFilExtn =

fileName.Remove(0, fileName.Length – 3);

// Save file

switch(strFilExtn)

{

case “bmp”:

curImage.Save(fileName, ImageFormat.Bmp);

break;

case “jpg”:

curImage.Save(fileName, ImageFormat.Jpeg);

break;

case “gif”:

curImage.Save(fileName, ImageFormat.Gif);

break;

case “tif”:

curImage.Save(fileName, ImageFormat.Tiff);

break;

case “png”:

curImage.Save(fileName, ImageFormat.Png);

break;

default:

break;

}

}

}

First save dialog box is shown with acceptable extensions. Then from the file name returned by the dialog box the extension is retrieved and according to the extension Save() method is called with corresponding image format parameter.

Next comes the handler for mnu200Zoom. But before that certain things have to be done. First add the following line to the class at the application level:

private double curZoom = 1.0;

Then the mnuLoad (from previous part) has to be changed slightly. The added code is shown in bold:

private void mnuLoad_Click(object sender,

System.EventArgs e)

{

//Change the AutoScrollMinSize property

this.AutoScrollMinSize = new Size

((int)(curImage.Width * curZoom),

(int)(curImage.Height * curZoom));

// Create OpenFileDialog

OpenFileDialog opnDlg = new OpenFileDialog();

// Set a filter for images

opnDlg.Filter =

“All Image files|*.bmp;*.gif;*.jpg;*.ico;”+

“*.emf;,*.wmf|Bitmap Files(*.bmp;*.gif;*.jpg;”+

“*.ico)|*.bmp;*.gif;*.jpg;*.ico|”+

“Meta Files(*.emf;*.wmf;*.png)|*.emf;*.wmf;*.png”;

opnDlg.Title = “ImageViewer: Open Image File”;

opnDlg.ShowHelp = true;

// If OK, selected

if(opnDlg.ShowDialog() == DialogResult.OK)

{

// Read current selected file name

curFileName = opnDlg.FileName;

// Create the Image object using

// Image.FromFile

try

{

curImage = Image.FromFile(curFileName);

}

catch(Exception exp)

{

MessageBox.Show(exp.Message);

}

}

// Repaint the form, which forces the paint

// event handler

Invalidate();

}

What the added code does is that it multiples the image width and height with zoom factor to render an image with appropriate zoom setting. Next the paint event handler has to be changed. The DrawImage() method has to be changed into the following:

g.DrawImage(curImage, new Rectangle

(this.AutoScrollPosition.X,

this.AutoScrollPosition.Y,

(int)(curRect.Width * curZoom),

(int)(curRect.Height * curZoom)));

The image should have the height and width according to the zoom factor. For that the current width and height is multiplied with the current zoom factor represented by curZoom variable. Last step in zooming is the event handler for mnu200Zoom:

private void mnu200_Click(object sender,

System.EventArgs e)

{

if(curImage != null)

{

curZoom = (double)200/100;

Invalidate();

}

}

Lastly comes the event handler for the mnuThumbNail:

private void mnuThumbNail_Click(object sender,

System.EventArgs e)

{

if(curImage != null)

{

// Callback

Image.GetThumbnailImageAbort tnCallBack =

new Image.GetThumbnailImageAbort(tnCallbackMethod);

// Get the thumbnail image

Image thumbNailImage = curImage.GetThumbnailImage

(100, 100, tnCallBack, IntPtr.Zero);

// Create a Graphics object

Graphics tmpg = this.CreateGraphics();

tmpg.Clear(this.BackColor);

// Draw thumbnail image

tmpg.DrawImage(thumbNailImage, 40, 20);

// Dispose of Graphics object

tmpg.Dispose();

}

}

// Must be called, but not used

public bool tnCallbackMethod()

{

return false;

}

It first creates a variable of type GetThumbnailImageAbort and assign the tnCallbackMethod() to it by passing the method to the GetThumbnailImageAbort. Then it creates a new instance of Image class to hold the image returned by the GetThumbnailImage method, which is then used to draw the thumbnail onto the screen.

This brings us to the end of this discussion. In this part I discussed more advanced features of GDI+. I will continue along the same lines in next part. Till then…

Image manipulation is considered one of the difficult tasks in Graphics Programming. This is more so if the Operating System under consideration is Windows. The reason is that to achieve efficiency nothing can find a better option than Windows API. The APIs provided by Windows comes under the GDI roof. GDI is short for Graphics Device Interface. However, GDI works at low level graphics. So to use it good understanding of how Windows work is required. Even with MFC, GDI is a bit complicated. But with evolution of GDI+ the scenario is changing. The major change between GDI and GDI+ is that GDI exposes only APIs for unmanaged code whereas GDI+ provides for both managed as well as unmanaged code. Hence GDI+ is oriented towards .Net based languages. And in the world of .Net, one of the best options is C#. So in this discussion, I would be focusing on how to use the basic image manipulations using GDI+ library in C#. The first section would focus on the terminology used by GDI+ and image manipulation in general. In the second section I would detail the steps involved in image manipulation using GDI+. The last section would be about using the basic image manipulation within a real world application. That’s the outline for this discussion.

Image Manipulation using GDI+ – the Terminology:

Before understanding how to use GDI+ APIs for image manipulation, it is better to understand what GDI+ really is and what constitutes its vocabulary. If we consider basic image manipulation, there four points that need to be understood which are:

1. GDI+

2. Vector Graphics

3. Raster Graphics

4. Imaging

The second, third and fourth are integral part of image manipulation vocabulary. The fourth term in itself consists of many terms, some which would be discussed in this section.

1. GDI+:

By definition “GDI+ is a library that provides an interface that allows programmers to write Windows and Web graphics applications that interact with graphical devices such as printers, monitors, or files”. In essence, it provides a layer of abstraction over varied devices with which GUI needs to work. This abstraction works by converting the data into device compatible form. The device then turns the data into human readable form. The implementation of GDI+ is in the form of C++ classes that can be used from .Net environment. In .Net library, GDI+ classes are exposed through the System.Drawing and its namespaces.

2. Vector Graphics:

By definition “Graphics in which an image is stored as a series of numbers defining size, position, and shape”. In other words, in Vector graphics the image contains position vectors describing the image, where a vector represents both quantity and direction. For example, instead of containing a bit in a file for say a line being drawn, a vector graphics file describes a series of points to be connected. Also since the file contains vector commands, the size of the file becomes small.

3. Raster Graphics:

Raster is grid of x- and y- co-ordinates on a display space which, in this case is the display device. A Raster Graphics file contains information identifying which of these coordinates to illuminate using monochrome (black and white) or spectro-chrome (color) values. Raster images are also known as Bit-mapped images or Bitmaps because it contains information directly mapped to the display device’s grid. Due to this they have larger size than that of Vector Graphic files.

4. Imaging:

Imaging is the process of viewing and manipulating the images. In managed GDI+, imaging has two facets – Basic and Advanced. The processes in Basic covers loading and saving, zooming, scaling etc. whereas that in Advanced covers color palette, metafiles and tagged images. The basic functionalities are encapsulated in Image class. It provides methods to load, save and create images. The subclasses of Image class i.e. Bitmap and Metafile provides the functionality for displaying and manipulating the images.

That was a bird’s eye view of the recurring terms in the world of GDI+ and image manipulation. In the next section I will discuss about steps in loading and manipulating it using flipping as well as rotating the image.

Image Manipulation Using GDI+ – Step By Step:

Now lets have a look at steps at using the GDI+ for image manipulation. There are four main steps:

1. Overriding the Paint event of Form

2. Obtaining the Graphics object

3. Obtaining the Image object

4. Apply manipulation methods

First two steps is required whenever one has to work with GDI+. However, the last two comes into picture only when image manipulation has to be done. So here are the details:

1. Overriding the Paint event of form:

In .Net applications, drawing is done on a Form’s surface. This is same for both web applications as well as desktop application. The function where the actual drawing code can be hooked into is the Paint method. So, first step is to override this method to get foothold into the draw process within the application. This can be achieved either by supplying Paint handler to the Form’s paint method or by overriding OnPaint method. Though its better to provide Paint handler.

2. Obtaining the Graphics object:

The next step is to obtain the object of Graphics class. Because only through the Graphics object that the drawing methods can be called. To show an image it has to be drawn on to the Form. That can only be done using the DrawImage method of an object of Graphic class. An object of Graphics object is associated with a form. So it can be obtained by-

a. Using PaintEventArgs argument passed into the Paint handler

b. Using PaintArgs argument passed into the OnPaint method

c. Using CreateGraphics method of a Form.

The example of the first method would be thus:

private void form1_Paint(object sender, PaintEventArgs e)

{

Graphics g = e.Graphics;

}

where form1_Paint is the handler for Paint event of Form form1. The Graphics property of the PaintEventArgs returns a Graphics object associated with the Form object. The second way can be exemplified as :

protected override void OnPaint(PaintEventArgs e)

{

Graphics g = e.Graphics;

}

The main difference between first and second is that in the second way, the OnPaint method is overridden. The example for the third way would be :


Graphics g = this.CreateGraphics();

Where this refers to the Form in which this statement has been embedded. The CreateGraphics() method returns a method object of the Form. The only drawback of this approach is that the Graphics object would have to be disposed manually using dispose method of the Graphics object. I prefer the first method. Hence I would be using it henceforth.


3. Obtaining the Image object:

Image object can be obtained in three different ways using the static methods provided by Image class:

a. FromFile method returns an Image from the file specified as argument.


b. FromHbitmap creates and returns an Image object from a window handle to a

bitmap.


c. FromStream creates an Image object from a stream of bytes (in a file or a

database).


Among these, the first method is most commonly used if the image is available as a file, whereas the third method comes handy in web applications where the images would be stored as a column value of a table. The following statements creates an object of Image class using the first method:


Image curImage = Image.FromFile(“flower.bmp”);


4. Apply manipulation methods:

For the sole purpose of imaging, the Image class provides many methods. The most common in imaging are Flip and Rotate. The Image class provides for these by the following method – RotateFlip. The parameter to this method defines whether the flip as well as rotation both has to be done or only one of flip and rotation has to be applied. It also defines through what degrees flip or rotate has to be done. The following table provides most common arguments to the RotateFlip method as well as their description. The argument being passed are the members of RotateFlipType.




Member

Description

Rotate180FlipNone

180-degree rotation without flipping

Rotate180FlipX

180-degree rotation with a horizontal flip

Rotate180FlipXY

180-degree rotation with horizontal and vertical flips

Rotate180FlipY

180-degree rotation with a vertical flip

Rotate270FlipNone

270-degree rotation without flipping

Rotate270FlipX

270-degree rotation with a horizontal flip

Rotate270FlipXY

270-degree rotation with horizontal and vertical flips

Rotate270FlipY

270-degree rotation with a vertical flip

Rotate90FlipNone

90-degree rotation without flipping

Rotate90FlipX

90-degree rotation with a horizontal flip

Rotate90FlipXY

90-degree rotation with horizontal and vertical flips

Rotate90FlipY

90-degree rotation with a vertical flip

RotateNoneFlipNone

No rotation and no flipping

RotateNoneFlipX

No rotation, with a horizontal flip

RotateNoneFlipXY

No rotation, with horizontal and vertical flips

RotateNoneFlipY

No rotation, with a vertical flip


For example to flip and rotate an image through 90 degrees on both X- and Y- axes the statement would be:


curImage.RotateFlip(RotateFlipType.Rotate90FlipXY);


where curImage is an object of the type Image and the argument tells the RotateFlip method that the image has to be rotated through 90 degrees and need to be flipped both vertically and horizontally.


That brings us to the end of this section. In the next section I would develop an application that would load, flip and rotate the loaded image.


Image Manipulation – In the Real World:

Starting from this part onwards, I would be developing an image manipulation application using C# and GDI+. The basic version that would be developed now would provide the following features:


1. Load an image file

2. Rotate the loaded image 90 degrees

3. Flip the image along X- axis


First the menus have to be setup as shown in the figure below.

Fig1 – GDI_1.jpg

Fig2 – GDI_1_RotateFlip.jpg


The names of the menus are:


mnuLoad – the submenu that would show the Open dialog box and loads the file

mnu90rotate – rotates the image by 90 degrees.

mnuXfilp- flips the image along X-axis.


Here is the handler for loading the image:


private void mnuLoad_Click(object sender,

System.EventArgs e)

{

// Create OpenFileDialog

OpenFileDialog opnDlg = new OpenFileDialog();

// Set a filter for images

opnDlg.Filter =

“All Image files|*.bmp;*.gif;*.jpg;*.ico;”+

“*.emf;,*.wmf|Bitmap Files(*.bmp;*.gif;*.jpg;”+

“*.ico)|*.bmp;*.gif;*.jpg;*.ico|”+

“Meta Files(*.emf;*.wmf;*.png)|*.emf;*.wmf;*.png”;

opnDlg.Title = “ImageViewer: Open Image File”;

opnDlg.ShowHelp = true;

// If OK, selected

if(opnDlg.ShowDialog() == DialogResult.OK)

{

// Read current selected file name

curFileName = opnDlg.FileName;

// Create the Image object using

// Image.FromFile

try

{

curImage = Image.FromFile(curFileName);

}

catch(Exception exp)

{

MessageBox.Show(exp.Message);

}

}

// Repaint the form, which forces the paint

// event handler

Invalidate();

}

The name of the file returned by the open file dialog is set as the current file name. then it is used to load the file using the FromFile function of the Image class. The returned image is assigned to the curImage variable which is of type Image and its scope is of class level. Next Inavlidate() method is called so that paint event is fired. Following the code, embedded in the Form’s Paint method handler, that actually draws the image:


private void Form1_Paint(object sender,

System.Windows.Forms.PaintEventArgs e)

{

Graphics g = e.Graphics;

if(curImage != null)

{

// Draw image using the DrawImage method

g.DrawImage(curImage,

AutoScrollPosition.X,

AutoScrollPosition.Y,

curImage.Width,

curImage.Height );

}

}


The logic checks whether the current image is null or not. If not null, the DrawImage method of Graphic class is given the curImage as the Image to be loaded. The Graphic object is obtained from the Graphics property of PaintEventArgs object. Next the handler for rotating the image – it is embedded in the handler for the mnu90rotate menu item. Here is the code:


// Rotate 90 degrees

private void mnu90rotate_Click(object sender,

System.EventArgs e)

{

if(curImage != null)

{

curImage.RotateFlip(

RotateFlipType.Rotate90FlipNone);

Invalidate();

}

}


The image is rotated using the RotateFlip method of Image class. Since curImage is of Image class type, hence the method can be directly called on the Image object. Next it calls the Inavlidate() method to redraw the image.


Last comes the flip logic. It is embedded in the handler for the mnuXflip menu item.


// Flip X

private void mnuXflip_Click(object sender,

System.EventArgs e)

{

if(curImage != null)

{

curImage.RotateFlip(

RotateFlipType.RotateNoneFlipX);

Invalidate();

}

}

That’s all for the example. This sets up the stage for image manipulation using GDI+ in C#. However certain aspects of the problem of image manipulation have been left out including saving the image and how GDI+ works. These will be the topics that will be tackled in the next part of this series. Till then…

Note: You can find the article on: http://www.aspfree.com/c/a/C-Sharp/Basic-Image-Manipulation-using-GDI-and-C/

Follow

Get every new post delivered to your Inbox.