In the last part I gave you a bird’s eye view of what GDI+ is and showed you how to set up an application for using GDI+. However, without knowing more about the facilitator (i.e. GDI+), going forward would be counterproductive. Hence, in this part, along with different image manipulation techniques, the focus will be on the essentials of GDI+.
Contributed by A.P.Rajshekhar Rating: / 14 October 31, 2006
In the first section of this article, I will discuss GDI+ in brief, including the differences from its predecessor, that is GDI. The second section will focus on the manipulation techniques such as zooming and creating thumbnails. In the third and fourth sections I will extend the application created in the 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, and others that have graphical capabilities without going into low-level details of these devices. The essence of GDI+ is that it can interact with peripherals such as monitors and present data in human readable form. From the point of view of a developer, it's a monumental task to interact directly with these devices.
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 comes under GDI+. It does everything from printing a "Hello World" program on the console to drawing lines, rectangles and so forth and printing a form. Pictorially it can be shown thus:
The next question that arises is how does GDI+ work? To make it crystal clear, let's look at an example of drawing a line. A line, in essence, is a sequence of pixels from a starting location (X0, Y0) to an ending location (Xn, Yn). To draw such a line the devices (the monitor in this case) need to know the device coordinates 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 a device understandable form and vice versa.
So that’s an overview of how GDI+ works. Let's now move on to the topic of image manipulation.
In the last part, I discussed simple manipulations such as flipping and rotating. Lets tackle some topics that are a bit more complex in concept if not in implementation. They are:
Creating thumbnails.
Zooming a loaded image.
Saving a manipulated image.
Of these the first two come under the category of image manipulation (strictly speaking) whereas the last is a generic image-based task along the lines of a file operation.
Creating Thumbnails
Thumbnails are reduced versions of images. The dimensions of a thumbnail image are typically of 80 to 200 pixels in length. In GDI+, a thumbnail of an image can be created by GetThumbnailImage() of Image class. It takes four parameters.
The first parameter corresponds to the width. The 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 it is not being used in the current version. The fourth parameter is likewise not used, but needs to be passed for compatibility. The fourth parameter must be IntPtr.Zero.
If the first two parameters, namely width and height, are 0, then GDI+ returns an embedded thumbnail (some images contain thumbnails embedded into them). Otherwise the thumbnail is created using system-defined dimensions. For example if img is an instance of the Image class and the width and height to be used are system defined, the statement to create a thumbnail would be:
where thumbNailImage contains the returned thumbnail, and the tnCallback is a function corresponding to Image.GetThumbnailImageAbort which is defined as:
// Must be called, but not used
style='font-size:10.0pt;font-family:Verdana'>public bool tnCallbackMethod()
{
return false;
}
Zooming a loaded image
Zooming is the process of enlarging an image by multiplying it with a number called the 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.” By dividing the current size of the image with the 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.
Saving a manipulated image
Saving 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 the Save() method of the Image class abstracts out all the details of writing out the data 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 the ImageFormat class. The 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 ImageFormatobject.
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 these in detail in the future.
To give you an example, if you wanted to save an 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 the Image class.
That brings us to the end of this section. In the next section, I will be extending the application developed in the first part by embedding the operations discussed in this section.
It is time to put the theory into practice. I will do this by enhancing the application developed in the last part by adding the following functionalities:
Save the image in the format specified by user.
Zoom in according to the percentage selected from the menu.
Create a 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 the 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 the save dialog box is shown with acceptable extensions. Then, from the file name returned by the dialog box, the extension is retrieved. Finally, according to the extension, the Save() method is called with the 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();
}
The added code multiples the image width and height with the zoom factor to render an image with the appropriate zoom setting. Next the paint event handler has to be changed. The DrawImage() method has to be changed into the following:
The image should have the correct 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. The 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();
}
}
Finally we have 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 assigns the tnCallbackMethod() to it by passing the method to the GetThumbnailImageAbort. Then it creates a new instance of the 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…