Drawing Using Images


Images have many uses in GDI+. Of course, you can draw images into your windows, but you can also create a brush (TextureBrush) with an image and draw shapes that are then filled in with the image. You can create a pen from the TextureBrush and draw lines using the image. You can supply a TextureBrush when drawing text, and the text will then be drawn using the image. the Image class is in the System.Drawing namespace.

Another very important use of images is the graphics programming technique of double-buffering. Sometimes the drawing you wish to create is very elaborate and intricate and takes quite a bit of time to draw, even with today's fast machines. It is not a pleasing effect to see the graphic "creep" onto the screen as it is being drawn. Examples of these types of applications are mapping applications and complex CAD/CAM applications. In this technique, instead of drawing into a window, you draw into an image, and after drawing into the image is completed, you draw the image to the window. This is the technique known as double-buffering. Certain other graphics techniques involve drawing in layers, where first you draw the background, then you draw objects on top of the background, and finally you draw some text on top of the objects. If this drawing is done directly to the screen, the user will see a flickering effect. Double-buffering eliminates this flickering effect. You look at a double-buffering example a bit later.

Image itself is an abstract class. There are two descendants of Image: Bitmap, and Metafile.

The Bitmap class is a general-purpose image, with height and width properties. The examples in this section will use the Bitmap class. You will load a Bitmap image from a file and draw it. You will also create a brush from it and use that brush to create a pen to draw lines, and also use that brush to draw some text.

You look at the Metafile class near the end of this chapter, when you see an overview of the advanced capabilities of GDI+.

Important

Always call Dispose( ) on Image objects.

It is important to call Dispose() on Image objects that you create, or use the using blocks, otherwise your application may deplete the Windows resources.

There are several possible sources for a bitmap. You can load the bitmap from a file or create the bitmap from another existing image, or it can be created as an empty image, onto which you can draw. When you read the image from a file, it can be in the JPEG, GIF, or BMP format. In the following Try It Out, you will be able to see how to read an image from a file, and draw it in a window.

Try It Out – Image Example

image from book

The following is a very simple example to read an image from a file and draw it in a window.

  1. Create a new Windows application called DrawImage in the directory C:\BegVCSharp\ Chapter30.

  2. First, you need to declare a private variable in your Form1 class to hold the image after you read it from a file. After the declaration of the components variable, add the declaration of theImage as follows:

    partial class Form1 : Form { private Image theImage; 
  3. Modify the constructor so that it appears as follows:

    public Form1() {    InitializeComponent(); SetStyle(ControlStyles.Opaque, true); theImage = new Bitmap("Person.bmp"); } 
  4. Now, add an OnPaintEvent() method to your class:

     protected override void OnPaint(PaintEventArgs e) { Graphics g = e.Graphics; g.DrawImage(theImage, ClientRectangle); } 
  5. Finally, you need to dispose of the Image that is stored in a member variable of your class. Modify the Dispose() method of the form class. Note that with Visual Studio 2005, the Dispose() method is in the file Form1.Designer.cs. The easiest way to open the file is to click the Show All Files button on the toolbar at the top of the Solution Explorer window. Here is the function:

    protected override void Dispose( bool disposing ) { if (disposing) { theImage.Dispose(); }    if (disposing && (components != null))    {      components.Dispose();    }    base.Dispose(disposing); }

Before running this example, you must get the BMP file called Person.bmp and place it in the DrawImage\ bin\Debug directory. the Person.bmp file can be found in the code download, or you can use another bitmap you have, but remember to change the line

theImage = new Bitmap("Person.bmp");

that you added in step 3 to hold the filename of your own bitmap.

When you compile and run this code, you should see the display shown in Figure 30-12.

image from book Figure 30-12

How It Works

In the constructor, you instantiate a Bitmap object and assign it to the Image variable you declared.

Then, in the OnPaint() method, you draw the image. When you draw the image, you pass a Rectangle as one of the arguments to the DrawImage() method. If the image is not the same size as the rectangle that you pass to DrawImage(), GDI+ automatically resizes the image to fit in the specified rectangle. One way to enforce that GDI+ will not resize the image is to pass the size of the image, retrieved from the Width and Height properties, to the DrawImage() method.

image from book

Drawing with a Texture Brush

You will now create a TextureBrush from the image you have just used, and look at the following three different examples of its use:

  • Drawing an ellipse

  • Creating a Pen

  • Drawing text

In the following Try It Out, you start with the previous code example and make a few modifications to it.

Try It Out – Drawing an Ellipse with an Image

image from book

Follow these steps to see drawing some text using a TextureBrush:

  1. Starting with the code in the previous DrawImage example, add another Image variable declaration to the Form1 class:

    partial class Form1 : Form {    private Image theImage; private Image smallImage; 
  2. In the form's constructor, create smallImage from theImage. When you create it, you specify a rectangle that is half the height and half the width of theImage. This creates a smaller version of the original image:

    public Form1() {    InitializeComponent();    SetStyle(ControlStyles.Opaque, true);    theImage = new Bitmap("Person.bmp"); smallImage = new Bitmap(theImage, new Size(theImage.Width / 2, theImage.Height / 2));     }

  3. Replace the OnPaint() method with this one:

     protected override void OnPaint(PaintEventArgs e) { Graphics g = e.Graphics; g.FillRectangle(Brushes.White, ClientRectangle); Brush tBrush = new TextureBrush(smallImage, new Rectangle(0, 0, smallImage.Width, smallImage.Height)); g.FillEllipse(tBrush, ClientRectangle); tBrush.Dispose(); } 

  4. Finally, you need to dispose of the two images that are stored in member variables of your class. Modify the Dispose() method of the class (in Form1.Designer.cs) as follows:

    protected override void Dispose( bool disposing ) { if (disposing) { theImage.Dispose(); smallImage.Dispose(); }    if (disposing && (components != null))    {      components.Dispose();    }    base.Dispose(disposing); }
  5. When you run this application, the window looks like Figure 30-13.

    image from book
    Figure 30-13

How It Works

When you create the TextureBrush, you pass a rectangle to the constructor to specify what part of the image will be used for the brush. In this case, you specify that you will use the entire image. Whatever is drawn using the TextureBrush uses the bitmap instead of a solid color.

Most of the code for this example has already been explained in this chapter. The difference is that you call the FillEllipse() method of the Graphics class, passing your newly created texture brush, and the ClientRectangle draws the ellipse in the window:

g.FillEllipse(tBrush, ClientRectangle);

In the following Try It Out, you will create a TextureBrush, then create a pen from the TextureBrush.

image from book

Try It Out – Creating a Pen from an Image

image from book

Now that you have created a TextureBrush, you can create a pen using that brush.

  1. Starting with the code in the DrawImage example that you modified in the previous Try It Out, change the OnPaint() method so that it looks like this:

    protected override void OnPaint(PaintEventArgs e) {    Graphics g = e.Graphics;        g.FillRectangle(Brushes.White, ClientRectangle);        Brush tBrush = new TextureBrush(smallImage, new Rectangle(0, 0,                   smallImage.Width, smallImage.Height)); Pen tPen = new Pen(tBrush, 40); g.DrawRectangle(tPen, 0, 0,  ClientRectangle.Width, ClientRectangle.Height); tPen.Dispose();    tBrush.Dispose(); }

  2. When you run this code, it looks like Figure 30-14.

    image from book
    Figure 30-14

In the following Try It Out, having created a TextureBrush, you will draw some text with it.

image from book

Try It Out – Drawing Text with an Image

image from book

You can draw text using the TextureBrush also.

  1. Continuing with the code from the previous Try It Out, modify the OnPaint method as follows:

    protected override void OnPaint(PaintEventArgs e) {    Graphics g = e.Graphics;    g.FillRectangle(Brushes.White, ClientRectangle);        Brush tBrush = new TextureBrush(smallImage, new Rectangle(0, 0,                   smallImage.Width, smallImage.Height)); Font trFont = new Font("Times New Roman", 32, FontStyle.Bold | FontStyle.Italic); g.DrawString("Hello from Beginning Visual C#",  trFont, tBrush, ClientRectangle); tBrush.Dispose(); trFont.Dispose(); }

  2. For this example, you'll actually use a different bitmap — change the line in the form constructor that sets the source for theImage to:

     theImage = new Bitmap("Tile.bmp"); 

    The Tile.bmp file can also be found in the download code.

  3. When you run this code, it appears as shown in Figure 30-15.

    image from book
    Figure 30-15

image from book

The call to the DrawString() method is similar to previous uses of that method. It takes as arguments the text, the font, your texture brush, and a bounding rectangle:

g.DrawString("Hello from Beginning Visual C#",              trFont, tBrush, ClientRectangle); 

Double-Buffering

I previously touched on the problems when drawing takes too long and the user has to wait a long time to see the graphics drawn. As I explained before, the solution is to draw into an image, and when you have completed all drawing operations, draw the complete image to the window.

In the following Try It Out, you can see the performance benefits of double-buffering. By not drawing to the screen until fully ready, you can improve the performance profile of your application.

Try It Out – Double-Buffering Example

image from book

Follow these steps to create an application that has performance issues:

  1. Create a new Windows application called DoubleBuffer, and add the following OnPaint() method, which a draws large number of lines in random colors.

     protected override void OnPaint(PaintEventArgs e)  { Graphics g = e.Graphics; Random r = new Random(); g.FillRectangle(Brushes.White, ClientRectangle); for (int x = 0; x < ClientRectangle.Width; x++) { for (int y = 0; y < ClientRectangle.Height; y += 10) { Color c = Color.FromArgb(r.Next(255), r.Next(255), r.Next(255)); using (Pen p = new Pen(c, 1))  { g.DrawLine(p, new Point(0, 0), new Point(x, y)); } } } } 

  2. When you run this, you can see the drawing take place before your eyes (that is, if your machine is not too fast). After all the drawing is completed, the window looks like Figure 30-16.

    image from book Figure 30-16

  3. Now you add the double-buffering — if you replace the OnPaint() method with this version, the graphics are drawn all at once, after a second or two:

    protected override void OnPaint(PaintEventArgs e) { Graphics displayGraphics = e.Graphics;    Random r = new Random(); Image i = new Bitmap(ClientRectangle.Width, ClientRectangle.Height); Graphics g = Graphics.FromImage(i);        g.FillRectangle(Brushes.White, ClientRectangle);        for (int x = 0; x < ClientRectangle.Width; x++)    {       for (int y = 0; y < ClientRectangle.Height; y += 10)       {          Color c = Color.FromArgb (r.Next(255), r.Next(255), r.Next(255));          Pen p = new Pen(c, 1);          g.DrawLine(p, new Point(0, 0), new Point(x, y));          p.Dispose();       }    } displayGraphics.DrawImage(i, ClientRectangle); i.Dispose(); }

How It Works

The part of the code responsible for drawing the lines is straightforward — you saw the DrawLine() method earlier in the chapter. The only real thing of note in this part of the code is the FromArgb() static Color method, which creates a Color struct from the three supplied integer values, corresponding to the red, green, and blue parts of the color.

In the double-buffering code, (step 3), you create a new image that has the same height and width of the ClientRectangle with the following line:

Image i = new Bitmap(ClientRectangle.Width, ClientRectangle.Height);

You then get a Graphics object from the image using the following line:

Graphics g = Graphics.FromImage(i);

All of the drawing operations are the same as the previous code, except that they now draw into the image instead of directly onto the window.

Finally, at the end of the function, you draw the image to the window:

displayGraphics.DrawImage(i, ClientRectangle);

Because the lines are drawn first to an invisible image, you do have to wait a short while before you see anything.

image from book




Beginning Visual C# 2005
Beginning Visual C#supAND#174;/sup 2005
ISBN: B000N7ETVG
EAN: N/A
Year: 2005
Pages: 278

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net