The .NET Framework makes it very easy to work with images of various sorts. The System.Drawing namespace has the abstract class Image that provides functionality for derived classes such as Bitmap and Metafile . A bitmap stores an image as an array of pixels. A metafile stores an image as a set of drawing commands. Drawing an Image The shared method FromFile in the Image class creates an image from a file whose file name is specified by a string. The DrawImage and DrawImageUnscaled methods of the Graphics class can be used to draw an image onto a drawing surface specified by a Graphics object. | The program BitmapDemo illustrates drawing an image that is loaded from a file. You can relocate the image by clicking the mouse, and you can change the image by using the File Open menu command. The standard file open dialog lets you navigate to a folder where the desired image file is stored. The FromFile method supports a number of standard image file formats, including GIF, TIFF, BMP, and JPEG. The Images directory of this chapter contains a few sample images in a number of different formats. | Public Class Form1 Inherits System.Windows.Forms.Form #Region " Windows Form Designer generated code " ... Private m_loc As Point Private m_image As Image Private m_file As String Protected Overrides Sub OnMouseDown(_ ByVal e As System.Windows.Forms.MouseEventArgs) m_loc = New Point(e.X, e.Y) Invalidate() MyBase.OnPaint(e) End Sub Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load m_file = "..\NET Logo.gif" m_image = Image.FromFile(m_file) m_loc = New Point(10, 10) BackColor = Color.White End Sub Protected Overrides Sub OnPaint(_ ByVal e As System.Windows.Forms.PaintEventArgs) Dim g As Graphics = e.Graphics g.DrawImageUnscaled(m_image, m_loc) End Sub Private Sub mnuFileExit_Click(_ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuFileExit.Click Close() End Sub Private Sub mnuFileOpen_Click(_ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuFileOpen.Click Dim dlg As New OpenFileDialog() dlg.InitialDirectory = "\OI\NetVb" Dim status As DialogResult status = dlg.ShowDialog() If status = DialogResult.OK Then m_file = dlg.FileName m_image = Image.FromFile(m_file) Invalidate() End If End Sub End Class Saving a Bitmapped Image Something else you might want to do with images is to create an image file. That is also easy to do with the .NET Framework. You can use drawing commands to create a bitmap by obtaining a Graphics object that is associated with a bitmap. You can then draw to this Graphics object using ordinary drawing commands, and you can then save the bitmap. The following steps outline the general procedure to be followed. A detailed code example is provided in the next section. -
Instantiate a Bitmap object. -
Obtain a Graphics object using the FromImage shared method of the Graphics class. -
Perform drawing commands on the Graphics object. -
Call the Save method of the Bitmap class to save the bitmap using a specified image file format. Drawing Program, Version 3 | We illustrate with Version 3 of our drawing program DrawDemo . We can draw rectangles and ellipses as before. We show the bounding rectangle of our drawing with dotted lines. A menu command File Save As Bitmap will save the image as both a BMP and a GIF file. Build and run the program. Figure 11-10 illustrates a drawing consisting of one rectangle and one ellipse. | Figure 11-10. Creating a bitmap using our drawing program. When you save, files drawing.bmp and drawing.gif will be created in the same directory as the program executable. You can then examine these files using standard graphics programs. If you have the standard file extension associations, you can double-click on these files in Windows Explorer. You will see the .bmp file displayed in Paint and the .gif file displayed in Internet Explorer. The program imports the System.Drawing.Imaging namespace and declares member variables to hold a bounding region, a dotted pen, and a bounding rectangle. Imports System.Drawing.Drawing2D Imports System.Drawing.Imaging ... Public Class Form1 Inherits System.Windows.Forms.Form ... Private m_region As New Region() Private m_dotpen As Pen Private m_bounds As Rectangle ... The load event handler initializes the region to be empty and the pen to be green with a dot style. Private Sub Form1_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ... m_region.MakeEmpty() m_dotpen = New Pen(Color.Green) m_dotpen.DashStyle = DashStyle.Dot End Sub A helper method UpdateRegion adds a rectangle determined by two points to the region with the Union method. A bounding rectangle is determined by the GetBounds method, which returns a RectangleF . The Round method is called to obtain the bounding rectangle as a Rectangle . Private Sub UpdateRegion(ByVal p As Point, _ ByVal q As Point) m_region.Union(MakeRectangle(p, q)) Dim g As Graphics = Me.CreateGraphics() Dim rectf As RectangleF rectf = m_region.GetBounds(g) m_bounds = Rectangle.Round(rectf) End Sub The OnMouseUp override updates the region using this helper method. Protected Overrides Sub OnMouseUp(_ ByVal e As System.Windows.Forms.MouseEventArgs) m_track = False m_shape = MakeShape(m_start, m_end, m_type) UpdateRegion(m_start, m_end) m_list.Add(m_shape) Invalidate() MyBase.OnMouseUp(e) End Sub OnPaint draws the bounding rectangle using the green dotted pen. Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs) Dim g As Graphics = e.Graphics Dim objShape As CShape For Each objShape In m_list objShape.DrawShape(g) Next g.DrawRectangle(m_dotpen, m_bounds) MyBase.OnPaint(e) End Sub Finally, the handler for the menu File Save as Bitmap saves the drawing to a bitmap the size and location of the bounding rectangle, using the steps outlined in the previous section. Private Sub mnuFileSaveBitmap_Click(_ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles mnuFileSaveBitmap.Click Dim bits As New Bitmap(m_bounds.Right, _ m_bounds.Bottom) Dim g As Graphics = Graphics.FromImage(bits) Dim rect As New Rectangle(0, 0, _ m_bounds.Right, m_bounds.Bottom) g.FillRectangle(Brushes.White, rect) Dim objShape As CShape For Each objShape In m_list objShape.DrawShape(g) Next bits.Save("drawing.bmp", ImageFormat.Bmp) bits.Save("drawing.gif", ImageFormat.Gif) End Sub Saving to a Metafile (Version 4) There is somewhat of a mismatch between the kind of drawing we are creating and the bitmap file formats used for saving the drawing. The drawing itself illustrates vector graphics, and the bitmaps use raster graphics. Another format provided by Windows, called a metafile , allows us to save drawing commands into a file in place of pixels. The resulting file will in general be much smaller than a BMP file. (The GIF file is also small, but it attains its small size through compression, which loses some fidelity. The metafile is an exact representation of the drawing.) The file format used is enhanced metafile with .emf extension. Like BMP, this file is Windows-specific. Version 4 of our DrawDemo program adds a menu command File Save as Metafile, which will save the drawing as the file drawing.emf in the same folder as the program executable. Here is the code for the menu handler. Private Sub mnuFileSaveMetafile_Click(_ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles mnuFileSaveMetafile.Click 'Set up the metafile Dim gVideo As Graphics = CreateGraphics() Dim dc As IntPtr = gVideo.GetHdc() Dim meta As New Metafile("drawing.emf", dc) gVideo.ReleaseHdc(dc) gVideo.Dispose() 'Draw to the metafile Dim g As Graphics = Graphics.FromImage(meta) Dim rect As New Rectangle(0, 0, _ m_bounds.Right, m_bounds.Bottom) g.FillRectangle(Brushes.White, rect) Dim objShape As CShape For Each objShape In m_list objShape.DrawShape(g) Next g.Dispose() End Sub This code is a little more complex than previous examples of GDI+, because working with Windows metafiles requires you to obtain a Windows device context , which up until now has been completely wrapped by the Graphics class. The GetHdc method obtains a handle to a device context (of the .NET data type IntPtr ), and this device context is used as a parameter to the Metafile constructor. A device context is an unmanaged resource, and so we must be careful to explicitly release it by a call to ReleaseHdc . The code also illustrates calling Dispose on our Graphics objects. We have not been careful with using Dispose in our GDI+ programs, but to make them robust, we should do so. We discuss memory management in GDI+ later in the chapter. The rest of the code is similar to drawing to a bitmap. We use another overloaded version of the Graphics method FromImage , this time passing a metafile rather than a bitmap. We can then use ordinary drawing commands. One difference from the bitmap case is that we have already made the association with the file, and so there is no save step. Viewing the Metafile We could write code to view the metafile using additional methods of the Metafile class. But we can view the metafile we've created very simply by using our previous BitmapDemo program. The Image class's shared method FromFile encapsulates many different kinds of file format, including enhanced metafile. Hence you can just run the BitmapDemo program and open the file drawing.emf , and you should see your drawing displayed. |