Graphics (Chapter 17)

The material I will discuss is mostly found in chapter 17.  I strongly recommend that you read the chapter to reinforce the material in the lecture and for additional detail.

Preliminaries

The following are some initial thoughts and ideas about graphics under .NET. 

  1. FCL supplies numerous graphics tools.  The primary namespaces in which these tools are contained are System.Drawing and System.Drawing.Drawing2D.  The tools supplied by .NET are relatively easy to use.

  2. The graphics tools that FCL supplies are collectively called the .NET resource GDI+.  GDI+ stands for an extension to the Graphical Device Interface.  This is a device independent graphical API.  This is probably not the interface for writers of game programs.  It would be too slow.  What do game programmers use?

  3. The following are some of the principle classes in the GDI+.

    1. Graphics contains methods to draw strings of characters, lines, rectangles, circles, and other shapes.  Why have strings of characters in a Graphics class?

    2. Pen is supplies to Graphics to draw figures.

    3. Brush is used to fill in shapes.

    4. Color a structure used to set the color components for Graphics methods.

    5. Font used to define fonts.

    6. Matrix used to specify transformations.  E.g. rotation, translation, and scaling.  If you have taken our graphics course, you know all about these.

  4. Technically, the GDI+ is implemented as a set of classes in unmanaged C++.  FCL supplies classes that are a wrapper for the GDI+.  Keep in mind, should the case every come up, that you can call unmanaged code from managed code.  This does take effort to do.

  5. The coordinate system for graphics is a bit unusual.  (.NET stays with a long established tradition.)  The origin is in the upper left-hand corner.   Positive "x" values are to the right of the origin and positive "y" values are below.  The units default to being in picture units - pixels.  This takes a little getting used to.  I will draw a picture in class.  See figure 17.2 in the text.

  6. When you use graphics, they are completely your responsibility.  This means that if the window you are using is minimized, resized, or overlaid, it is your responsibility to redraw the window.  WHY?  When such events occur, the CLR calls the OnPaint function in the object to redraw the screen.  Each object that allows graphics to be performed on it and has a virtual OnPaint function that you may override. 

  7. The OnPaint function should not be called directly, but instead should be called using the Invalidate method.  This will force the CLR to call the OnPaint method.  This is important in that the CLR supplies information about the area in which you are drawing.  So, if you call OnPaint directly, the CLR does not have a chance to supply this data.

  8. There is also a Paint event for which you can install event handler.  The Invalidate method will also cause this to be called.

  9. You usually draw in a form, but you can draw in other places.  For example, you can create "interesting" push buttons.

Graphics Contexts and Graphics object.

In order to perform graphical tasks, such as drawing graphs and color control, your code needs a graphics context.  The graphics context provides information on your drawing space.  It is managed by a Graphics object.  A graphics context is available for every object that is derived from System.Windows.Form.Forms.  There are two ways of getting the graphics context for an object.  These are by overriding the OnPaint function for the object and by creating a Paint event handler for the object.

The OnPaint function.

This guy will seem very familiar to people who know MFC.  MFC is obsolete and no longer taught at Ramapo College.  There is still legacy code in MFC and some folks who want their stuff to run on pre-XP operating systems may still used it.  MFC also has an OnPaint function.

To override the OnPaint function in C#, you use the following definition in your class:

            protected override void OnPaint( PaintEventArgs e )

The "override" means that this method will take the place of method with the same name and arguments in the parent class.  We don't use this keyword in unmanaged C++ virtual functions.

You get the Graphics object (which you will use to manage most graphical operations) from the PaintEventArgs argument as follows:

            Graphics graphicsOject = e.Graphics;

The graphicsOject may now be used to perform such tasks as drawing shapes.

OnPaint Event handler.

In keeping with the event driven nature of .NET windows applications, most programmer who want to perform graphics will install an event handler for the Paint event.  This has a nice side effect that .NET can still do whatever it wants when the OnPaint function is called.  By adding an event handler, we just add an additional function to be called.

We install an Paint event handler in the same manner that we install other event handlers.  Namely, we do it through the properties of the control for which we want this functionality.  Select events and then paint.  The Paint event handler will look like:

            protected void MyEventHandler_Paint( object sender, PaintEventArgs e )

You get a Graphics object in the same manner that you did with the OnPaint class.  Namely, you can get it from the e object.

Note: you must get a Graphics object each time that a Paint event occurs.  The Graphics object can change between one call and another.

Color Control

Color are represented in .NET as a 32 bit number.  The color is called an ARGB value.  Each letter stands for a different element of the color.  These elements are one byte in length and are represented by numeric values from 0 to 255.  The following are the meaning of the letters.

Question: Why does MS claim that .NET programmers have a choice of almost 17 million colors?

Note: if a graphic card does not support a particular color, the closest value will be selected.  This can also happen if you select a lower Color Quality.  You can do this under properties-settings for the screen.

Color is a struct that supports colors.  It has static constants for predefined colors.  This include all of the primary colors and quite a number of others.  It also has the following methods and properties:

Brushes

These are used to color the interior of various graphical shapes.  In the Graphics class, you can specify a method that has the word "Fill" followed by the shape.  This type of function requires a brush to fill the figure.    There are a number of different brush classes that are derived from Brush.  The one that we will usually use if Solid Brush.  Other brushes can be used to add texture.

The following is an example from the text that demonstrates various aspects of colors, figures and brushes.  ShowColors.cs.  Don't worry about the details of this program.  We will cover them later.  The main thing here is I want to demonstrate colors.

Notes on program:

  1. On the forms setup, I have a group boxes.  The group box is found in container.  It is a handy tool to group a set of controls together.
  2. For the back colors I have a textbox and a button.  For the front colors, I have 4 textboxes and a button.
  3. The class contains the structures frontColor and behindColor to name the two colors.   Let's look at initial values.
  4. This program overrides the OnPaint method of the Form class.  Button events will be used to change the colors.  All buttons and text boxes are named appropriately.
  5. If the user presses a button, it set a color and calls Invalidate.  The OnPaint method handles all drawing.
  6. Note we are using the static method Color.FromName to get a color by its name.  If you set color by name, an unknown name cause white to be displayed.  Let's verify.
  7. To set the ARGB colors requires 4 text boxes.  We use the static method Color.FromArgb to get the color from it numeric values.
  8. We are overriding OnPaint to draw the figures.  We will go through it in class.
  9. A white background is created by filling a rectangle in white.  The foreground and background rectangles are also create with the FillRectangle methods.
  10. Text is drawn using the DrawString method of the Graphics class.  This method has options that allow us to draw text strings starting at a point, in a rectangle.  We can also specify how the string will be drawn.  (E.g., vertical or horizontal.)   Note that we specify the font.
  11. Note that there are initial values for the front and back colors.

ColorDialog

This is a very cool component that allows the user to select colors from a palette of available colors.  It is very simple to use in a program and I will demonstrate by the text's example.  ShowColorsComplex.cs 

  1. There are as always quite a number of properties that we will not discuss fpr the ColorDialog class. 
  2. Note the use of the colorChooser.FullOpen = true; for the background colors.  (This gives you a full menu.) 
  3. Note that by setting the background color for the form, you are changing the colors of what it contains unless the user has set it in the properties box.  Change background color of a button and see that it perseveres.   So, there is an inheritance that comes into play.

Font Control

This material is important, but not very difficult.  I would like you to read section 17.5 and understand the examples.  Before leaving this topic, a couple quick notes:

  1. The text says that once a Font is created, its properties cannot be modified.  This isn't a big deal in that you can always create another font.
  2. The size of a Font is usually specified in pixels.  But it also may be defined in other units, including inches and millimeters.  The units are call design units.
  3. The DrawString method of the Graphics class allows the user to specify the color of the output using a Brush.  I would have expected a Pen instead.  Note that we used this.Font to use the current font.
  4. The FontFamily class provides information about a group of related fonts.  You can get an object of this type from each font.  This class provides information about the size of letters written using a font.

Example of creating a font:

FontStyle style = FontStyle.Italic;

Font myFont = new Font( "Arial", 12, style );   // style would be elements of the FontStyle enumeration.  Example bold

We can use this font in our programs.  Let's try it.

Drawing Lines, Rectangles and Ovals

The Graphics class provides methods for drawing lines, rectangles, and ovals.  It uses overloading to provide a variety of methods for each task.  Each method requires a pen to make line drawings and a brush to make solid figures.  If Draw is part of the function name, it means to draw lines.  If fill is part of the function name, it means to fill in the figure using a brush. 

For shapes, four int values are required.  Two for the coordinates of the upper left-hand corner and two to specify the shape's width and height. 

The following are methods to draw lines, rectangles, and ellipses.  They all are void functions and keep in mind that there are a number of overloads.

The following is the text's example of drawing these figures. LinesRectanglesOvals.cs  We will show how the positions of the lines are figured out.  We do have to be a lot more aware of pixels in this work.  Note that OnPaint is called on startup.  Note that a size class is used to specify the size of the class.  The Size class has Height and Width properties.

Question: How do we determine the origin and size of the area in which we will draw?  This is essential is we are drawing graphs.  This is using the ClientSize property of the object on which we are drawing.

Drawing Arcs (section 16.6)

In the interest of time, I will ask you to read this section.  The only knowledge you need for this section is to realize that you specify a start angle and a sweep angle.  Positive angles go clockwise with an angle of zero being the x - axis.

A practical set of examples.

GUI+ contains a lot of classes with a lot of members.  (What else is new. ;-) )There are also a number of classes to support the GUI+ classes.  I can't present them all.  You never know features that you need until you try and do a practical example.  I will present a number of new features by picking practical tasks and seeing how we can implement them.

Example1:

Write a program that will graph a set of points on the entire area of a form.  Before we begin, let as discuss the aspects of graphing a set of points.

  1. Usually if you graph a set of points, your will draw line segments between them.  We have the DrawLine method in the graphics class to draw lines.  You can draw points, but then you have to deal with more issues.
  2. We need to get the dimensions of the area within the control in which we can draw.  The ClientSize property returns a Size object that contains Width and Height properties which give us the dimensions of the space in which we can draw.  The upper left-hand corner of the client area has coordinates (0,0)  There is also a Size property in the forms class that returns the overall size.  The latter property is useless.
  3. The draw area is defines in pixels.  In class we will discuss how we will scale the points so that they fill the screen and we will discuss the problem of flipping the points to respect the weird y axis.
  4. To use the DrawLine method, you must specify a pen.  The second argument of the pen is the line width.  It defaults to 1 pixel.  You can draw fatter.  If you specify 1 or zero and you are using pixels, you will get 1 pixel wide lines.  If you specify other drawing units, then you will need to adjust.  There is a property DpiX of the Graphics class which us the dots (pixels) per inch.

Note: we can use the graphics object on most controls. graph1.cs

Notes on the code.  These duplication some of my preliminary comments.

  1. The area on which we graph is defined in pixels.  Recall that we can get the dimensions of the client area by the ClientSize property of the object on which we are drawing

    We can do differently.  At the end of the example, I graph a line using inches for the basic units.  I use the property PageUnit of the Graphics class and set it to the value of GraphicsUnit.Inch This will show some of the considerations in graphing in other units.  We can also set the PageUnit to millimeter and stranger units.
  2. To exercise the example, I create two arrays.  One for the x-values and the other for the corresponding y-values.
  3. I cleared the form to a specified color using the Clear method of the Graphics class.  It accepts the color as an argument.  Not much purpose for it here, but it was good to mention.  That is we are using the color associated with this class.
  4. In order to graph an object you need to determine the size of the area in which the graph will be drawn.  This may be accessed through the ClientSize property.    It returns a Size structure that has a Width and Height component.  Note: forms also has a Size member that returns the size of the entire form instead of just the drawing space.  I messed up my graph by using the size property the first time I tried to do graphics.
  5. There is issues in converting from "real world" coordinates to pixels.  There are ways that we can work around this, but the easiest way is to scale ourselves.  This was discussed in class and will be reviewed with this presentation.
  6. Note the issues of resizing the form.  Namely, this does not call Paint event.  (Much to my surprise.)  So we have to handle this by calling Invalidate from the resize event.  We will experiment with this.  Namely we will comment out the invalidate call.  This is annoying!
  7. Note the commented out call of the Dispose function.  This cleans up the graphics   You only have to do this if you created a new graphics object.
  8. We will show flicker in class and show how to address it with the discussion below.

Lab 11

Write a program to graph the SINE function for a range of values specified by the user.  (The range is a start and end x value for the graph.)  The graph you draw of the sine function should look smooth.  Make sure your graph does not overlap the range text box. 

Example2:

This example will add an x and a y axis to the graphs.  The following is the program: graph2.cs   This takes a lot more than you might at first suppose.  We will more than double the amount of code.  The following is a list of the aspects of this problem.  Don't be upset if you don't understand everything.

  1. Generally, people don't want the typical origin based x and y axes.  Instead they want axes along the left and bottom sides of the image.
  2. We must reduce the drawing area to make space for the axes.
  3. We must figure out how many labels we can put on each axis.  For the vertical and horizontal axes we need to know the height of the characters used in the labels.  Where can we get this?  The Height property for fonts provides us with this info.  I decided to label every 5th point.
  4. We must figure out the space taken by the labels.  There is a function in the Graphics classes called MeasureString that will do the job.  It will measure the size of a string as displayed.
  5. We must have vertical writing for the x axis labels.  This is an option of the DrawString method.
  6. We must put a tick on the axis to correspond to each label.  This is a straightforward calculation.
  7. We must figure out where each label is to be placed.
  8. We must make sure that the label corresponds to an actual point.
  9. Note: you don't really see output to the screen if you are in the debugger until your code returns from the paint function and maybe not even then.

Flicker Control - this is not in the text

In class we saw that certain drawing operations can cause the screen to flicker.  For example, increasing the size of a window.  (I will show this with our example.)  The flicker occurs due to conflicts between the draw and the refresh cycle.  The solutions is to have the draw go to a memory image and then move the data to the screen.  So, there is a single operation to drawing the screen.  Most graphics cards support this double buffering.

So, with a few lines, you can be a hero.

//
// TODO: Add any constructor code after InitializeComponent call
//

// Force double buffering.

// Indicates that double buffering is to be used.
this.SetStyle( ControlStyles.DoubleBuffer, true );

// When a control is painted, the functions OnPaint and OnPaintBackground are
// normally called.  This call indicates that the OnPaint function will handle everything.

this.SetStyle( ControlStyles.AllPaintingInWmPaint, true );

// Indicates that the control paints itself.
this.SetStyle( ControlStyles.UserPaint, true );

The bottom line is that if you put in these three lines in the proper place, flickering will be gone.  Why would you guess that the system defaults to having flickering?????

We will play with this in class and show how well it works with changing the size of an image.