Simple Graphics.

Visual C++ .NET provides a set of tools for drawing 2-dimensional shapes, displaying images, for controlling fonts and colors. These tools are parts of namespace System::Drawing and the other namespaces that together are called GDI+ (an extension of Graphical Device Interface). The System::Drawing namespace includes the following classes and some structures

The GDI+ coordinate system is shown on the picture below

GDI+ coordinate system
The upper-left corner of a GUI components (such as a Form) has coordinates (0,0). The x-coordinate is the horizontal distance from the upper-left corner. The y-coordinate is the vertical distance from the upper-left corner. Coordinate units are measured in pixels.

Colors

Structure Color defined in the System::Drawing namespace defines methods and constants to manipulate colors. Every color can be created from a combination of four components: red, green, and blue color components and opacity component alpha (value 0 specifies a transparent color, the value 255 - an opaque color). Value of each of these four components is an integer number in the range [0, 255]. The following list contains all predefined Color constants:
AliceBlue
AntiqueWhite
Aqua
Aquamarine
Azure
Beige
Bisque
Black
BlanchedAlmond
Blue
BlueViolet
Brown
BurlyWood
CadetBlue
Chartreuse
Chocolate
Coral
CornflowerBlue
Cornsilk
Crimson
Cyan
DarkBlue
DarkCyan
DarkGoldenrod
DarkGray
DarkGreen
DarkKhaki
DarkMagenta
DarkOliveGreen
DarkOrange
DarkOrchid
DarkRed
DarkSalmon
DarkSeaGreen
DarkSlateBlue
DarkSlateGray
DarkTurquoise
DarkViolet
DeepPink
DeepSkyBlue
DimGray
DodgerBlue
Firebrick
FloralWhite
ForestGreen
Fuchsia
Gainsboro
GhostWhite
Gold
Goldenrod
Gray
Green
GreenYellow
Honeydew
HotPink
IndianRed
Indigo
Ivory
Khaki
Lavender
LavenderBlush
LawnGreen
LemonChiffon
LightBlue
LightCoral
LightCyan
LightGoldenrodYellow
LightGray
LightGreen
LightPink
LightSalmon
LightSeaGreen
LightSkyBlue
LightSlateGray
LightSteelBlue
LightYellow
Lime
LimeGreen
Linen
Magenta
Maroon
MediumAquamarine
MediumBlue
MediumOrchid
MediumPurple
MediumSeaGreen
MediumSlateBlue
MediumSpringGreen
MediumTurquoise
MediumVioletRed
MidnightBlue
MintCream
MistyRose
Moccasin
NavajoWhite
Navy
OldLace
Olive
OliveDrab
Orange
OrangeRed
Orchid
PaleGoldenrod
PaleGreen
PaleTurquoise
PaleVioletRed
PapayaWhip
PeachPuff
Peru
Pink
Plum
PowderBlue
Purple
Red
RosyBrown
RoyalBlue
SaddleBrown
Salmon
SandyBrown
SeaGreen
SeaShell
Sienna
Silver
SkyBlue
SlateBlue
SlateGray
Snow
SpringGreen
SteelBlue
Tan
Teal
Thistle
Tomato
Transparent
Turquoise
Violet
Wheat
White
WhiteSmoke
Yellow
YellowGreen
Static values of the Color structure
The Color structure also has several methods that allow the programmers to define colors for drawing. These methods are
Method Description Example
FromArgb Creates a color based on red, green and blue components expressed as integers from 0 to 255. Overloaded version allows to specify alhpa, red, green, and blue values.
 my_color = Color::FromArgb(100, 0, 255, 0); 
FromName Creates a color from a name, passed as a pointer to a String
 my_color = Color::FromName(S"White"); 
FromKnownColor Creates a Color structure from the specified pre-defined color.
 my_color = Color::FromKnownColor(Color::Khaki); 
The public properties of the Color structure are

We can easily change colors of the form from inside the program, for example, in the constructor of the Form object. To illustrate this, let's get back to our first project and add the lines

this->ForeColor = Color::FromArgb(100, 0, 0, 255);
this->BackColor = Color::FromName( S"White" );
to the constructor Form1(void) to make it look like
Form1(void)
{
    this->ForeColor = Color::FromArgb(100, 0, 0, 255);
    this->BackColor = Color::FromName( S"White" );
    InitializeComponent();
}

Simple drawing

The class Graphics contains methods for drawing, color manipulation, displaying images and other graphics-related actions. Thus, in order to start drawing we need to have an instance of the Graphics class created. If we do all graphics-related stuff inside the OnPain() method, then we already have an object of this type created and passed as a attribute of the PaintEventArgs parameter. Thus, we need to extract this attribute from the argument e of the PaintEventArgs type:
Graphics *gObj = e->Graphics;
After this line variable gObj is available to draw shapes on the from.

If we want to create a event-driven graphics, then we need to associate the drawing with events like button clicks, or timer ticks, which do not have their own graphics context. In order to draw, we need to create an instance of the Graphics class by invoking the CreateGraphics() method. For example, inside a button click function it can be done like

Graphics *gObj = this->CreateGraphics();
where this refers to the Form object.

Once we created an object of the Graphics type we can start using its methods to draw simple pictures. Some of the methods are
Method Description
Clear(Color color) Clears the entire drawing surface and fills it with the specified background color.
gObj->Clear( this->BackColor );
DrawLine(*pen, x1, y1, x2, y2) Draws a line from (x1, y1) to (x2, y2). The pen parameter determines the color, style, and width of the line.
Pen *p = new Pen( Color::Blue ); 
gObj->DrawLine(p, 0, 0, 100, 100);
Overloaded
DrawRectangle(*pen, x, y, w, h) Draws a rectangle of the specified width w and height h. The top-left corner of the rectangle is located at the point (x, y). The pen parameter determines the color, style, and width of the rectangle.
gObj->DrawRectangle(p, 0, 0, 200, 100);
Overloaded.
DrawEllipse(*pen, x, y, w, h) Draws an ellipse inside the rectangle specified by the top-left corner (x, y), the width w, and the height h. The pen argument determines the color, style, and width of the ellipse.
gObj->DrawEllipse(p, 0, 0, 200, 100); 
Overloaded
DrawArc(*pen, x, y, w, h, start, sweep) Draws an arc representing a portion of an ellipse specified by a pair of coordinates, a width, and a height. The arc starts at the angle start (angle in degrees measured clockwise from the x-axis to the starting point of the arc) and the size of the arc is sweep (angle in degrees measured clockwise from the startAngle parameter to ending point of the arc).
gObj->DrawArc(p, 0, 0, 100, 100, 90, 30);
Overloaded
DrawPie(*pen, x, y, w, h, start, sweep) Draws a pie shape defined by an ellipse specified by a coordinate pair, a width, and a height and two radial lines.
gObj->DrawPie(p, 0, 0, 100, 100, 90, 30);
Overloaded
DrawPolygon(*pen, Point[]) Draws a polygon. The coordinates of each point are specified in an array of Point objects. This method draws a closed polygon, even if the last point is different from the first one.
Pen *pen = new Pen( Color::DarkBlue, 7);
Point points[] = new Point[4];
points[0].X =  50;  points[0].Y =   0;
points[1].X = 100;  points[1].Y =  50;
points[2].X =  50;  points[2].Y = 100;
points[3].X =   0;  points[3].Y =  50;
gObj->DrawPolygon(pen, points);
Overloaded
FillRectangle(*brush, x, y, width, heigth) Draws a solid rectangle of the given width and height. The top-left corner of the rectangle is located at the point (x, y). The brush attribute defines the fill pattern inside the rectangle.
Brush *brush = new Brush( Color::FromArgb(128, 128, 128, 128) );

gObj->FillRectangle(brush, 10, 10, 150, 40);
Overloaded
FillEllips(*brush, x, y, width, heigth) Draws a filled ellipse inside the given rectangle. Thebrush argument determines the fill pattern inside the ellipse.
gObj->FillEllipse(brush, 10, 10, 150, 40);
Overloaded
FillPie(*brush, x, y, w, h, start, sweep) Fills the interior of a pie section defined by an ellipse specified by a pair of coordinates, a width, and a height and two radial lines.
gObj->FillPie(brush, 0, 0, 200, 200, 0, -90);
Overloaded
FillPolygon(*brush, Point[]) Draws a closed solid polygon. The coordinates of each point are specified in an array of Points. Thebrush argument determines the fill pattern inside the polygon.
Brush *brush = new SolidBrush( Color::Blue );
Point points[] = new Point[4];
points[0].X =  50;  points[0].Y =   0;
points[1].X = 100;  points[1].Y =  50;
points[2].X =  50;  points[2].Y = 100;
points[3].X =   0;  points[3].Y =  50;
gObj->FillPolygon(brush, points);
Overloaded
Note that all angles are measured clockwise; that is, exactly the opposite to the usual trigonometry angles.

Positive and negative angles

Pens and brushes

You may have noticed that to draw any shape we need to have either a pen or a brush. In this section we will take a close look at these classes.

The Pen class defines an object used to draw lines and curves. A Pen object draws a line of specified width and style. Use the DashStyle property to draw several varieties of dashed lines. The line drawn by a Pen object can be filled in a variety of fill styles, including solid colors and textures. The fill style depends on brush or texture that is used as the fill object.

The list of some of the public attributes of the Pen class is
Attribute Description
Alignment  Gets or sets the alignment for this Pen object.
Brush  Gets or sets the Brush object that determines attributes of this Pen object.
Color  Gets or sets the color of this Pen object.
CustomEndCap  Gets or sets a custom cap to use at the end of lines drawn with this Pen object.
CustomStartCap  Gets or sets a custom cap to use at the beginning of lines drawn with this Pen object.
DashCap  Gets or sets the cap style used at the end of the dashes that make up dashed lines drawn with this Pen object
DashOffset  Gets or sets the distance from the start of a line to the beginning of a dash pattern
DashPattern  Gets or sets an array of custom dashes and spaces.
DashStyle  Gets or sets the style used for dashed lines drawn with this Pen object
EndCap  Gets or sets the cap style used at the end of lines drawn with this Pen object
PenType  Gets the style of lines drawn with this Pen object.
StartCap  Gets or sets the cap style used at the beginning of lines drawn with this Pen object
Width  Gets or sets the width of this Pen object.
This class provides several constructors that allow us to set Color, Width, and/or Brush attributes of the new Pen object during the creation. The simplest constructor, takes only the color of the new pen:

Pen *pen = new Pen( Color::FromArgb(100, 0, 0, 100) );
Another constructor allows the programmer to assign both the color and the width of the new pen:
Pen *pen = new Pen( Color::Blue, 3 );
And finally two more constructors allow us to use a Brush object to create a Pen. One of them takes a pointer to a Brush object only, and the other takes a pointer to a Brush and a width argument:
Pen *pen = new Pen( brush, 12);
The Brush property determines how the Pen object draws lines. Lines are drawn as if they are filled rectangles, with the characteristics of the specified Brush object.

Thus, we can specify in the constructor the color and the thickness of the lines we will draw using a pen. To control the style of the lines, we need to use property DashStyle. A DashStyle is an enumeration that represents the style used for dashed lines drawn with this Pen object. Possible values are
Value Description
Dash Specifies a line consisting of dashes
DashDot Specifies a line consisting of a repeating pattern of dash-dot
DashDotDot Specifies a line consisting of a repeating pattern of dash-dot-dot
Dot Specifies a line consisting of dots
Solid Specifies a solid line (the default value).
Custom Specifies a user-defined custom dash style. This value specifies that a custom pattern of dashes and spaces, defined by the DashPattern property, make up lines drawn with this Pen object. If the value of this property is DashStyle::Custom and the value of the DashPattern property is a null reference, the pen draws solid lines.
Please note that this enumeration is defined in the System::Drawing::Drawing2D namespace. Here is an example that shows how to draw a dark blue rectangle with thick dotted sides:

Pen *dot_pen = new Pen( Color::DarkBlue, 4);
dot_pen->DashStyle = DashStyle::Dot;
gObj->DrawRectangle(dot_pen, 10, 10, 200, 200);

The Brush class an abstract base class and cannot be instantiated. To create a brush object, we need to use classes derived from Brush, such as

Here are several examples that illustrate how we can use the different types of brushes to fill regions:

Brush *brush = new SolidBrush( Color::LightGreen );
gObj->FillPolygon(brush, points);
Solid Brush

Brush *hbrush = new HatchBrush( HatchStyle::ZigZag, Color::Navy, Color::Cyan );
gObj->FillPolygon(hbrush, points);
Hatch Brush
Please find more about possible values of the HatchStyle enumeration in MSDN library.

Bitmap *texture = new Bitmap(10, 10);                 // create a bitmap for future texture
Graphics *temp_gObj = Graphics::FromImage( texture ); // create a graphics object to fill the bitmap
SolidBrush *brush = new SolidBrush( Color::LightGreen );
temp_gObj->FillRectangle(brush, 0, 0, 10, 10);
brush->Color = Color::Blue;
temp_gObj->FillRectangle(brush, 2, 2, 7, 6);
brush->Color = Color::Navy;
temp_gObj->FillRectangle(brush, 1, 1, 4, 2);
Brush *tbrush = new TextureBrush(texture);    // create a texture brush
gObj->FillPolygon(tbrush, points);
Texture Brush

Brush *lgbrush = new LinearGradientBrush(Point(0, 50), Point(100, 50),
                                         Color::DarkBlue, Color::LightGreen );
gObj->FillPolygon(lgbrush, points);
Linear Gradient Brush

Drawing strings

Before we start discussing how to output a string in graphics context, we would like to shortly discuss the Font class. An object of this type keeps information about the font family, size, and style of the font to draw strings. The detailed information can be found in the MSDN library, here we only shortly mention the most important properties and constructors. Some attributes of the Font class are
Property Description
FontFamily Gets the FontFamily object associated with this Font object.
Bold Gets a value that indicates whether this Font object is bold.
Italic Gets a value that indicates whether this Font object is italic.
Strikeout Gets a value that indicates whether this Font object specifies a horizontal line through the font.
Underline Gets a value that indicates whether this Font object is underlined.
Style Gets style information for this Font object.
Height Gets the line spacing of this font.
Size Gets the em-size of this Font object measured in the unit of this Font object.
SizeInPoints   Gets the em-size, in points, of this Font object.
Unit Gets the unit of measure for this Font object.
These properties let us guess what we can initialize in the constructor. The Font class has a lot of overloaded constructors that allow programmers to specify different properties of the desired font. Here are some of them:
Constructor Description
Font(family, emSize); Initializes a new Font object using the specified family and size. The family parameter is a pointer to an object of the FontFamily class. An element of the FontFamily class can be created with specifying either a name of the font you would like to use
FontFamily *family = new FontFamily(S"Arial");
or by specifying one of the generic font families (these generic families can be retrieved by static public methods of the FontFamily class: GenericMonospace(), GenericSansSerif(), and GenericSerif()):
Drawing::Font *fnt = new Drawing::Font(FontFamily::GenericSansSerif, 32);
Font(familyName, emSize); Similar to the previous one. We specify the family and the size, but in this case we use the name instead of a FontFamily object. The name of the family is to be contained in a String object. For example,
Drawing::Font *fnt = new Drawing::Font(S"Times New Roman", 32);
Font(family, size, style); Similar to the first constructor, but allows to assign a style of the font. The argument style is a parameter of the FontStyle enumeration type. The possible values of FontStyle are:
Value Description
Bold Bold text.
Italic Italic text.
Regular Normal text.
Strikeout Text with a line through the middle.
Underline Underlined text.
Here is an example,
Drawing::Font *fnt = new Drawing::Font(FontFamily::GenericSansSerif, 32,
                                       FontStyle::Italic);
we an also combine the styles
Drawing::Font *fnt;
fnt = new Drawing::Font(FontFamily::GenericSansSerif, 32,
                        static_cast<FontStyle>(FontStyle::Bold + FontStyle::Underline));
Font(familyName, size, style); This constructor is similar to the previous one, but the family of the font is specified by a string, not an object.
Drawing::Font *fnt = new Drawing::Font(S"Courier", 32, FontStyle::Italic);
Font(family, size, style, unit); This constructor allows to specify the units we are measuring the size of the font in. The unit parameter has the GraphicsUnit enumeration type. The possible values are
ValueDescription
Display Specifies 1/75 inch as the unit of measure.
Document Specifies the document unit (1/300 inch) as the unit of measure.
Inch Specifies the inch as the unit of measure.
Millimeter Specifies the millimeter as the unit of measure.
Pixel Specifies a device pixel as the unit of measure.
Point Specifies a printer's point (1/72 inch) as the unit of measure.
World Specifies the world unit as the unit of measure.
For example, we can set the size of the font in inches:
Drawing::Font *fnt = new Drawing::Font(FontFamily::GenericSansSerif, 2,
                                       FontStyle::Italic, GraphicsUnit::Inch);
Font(familyName, size,
     style, unit);
We can also specify the measuring units when we assign the font family by name.

Now, when we know how to create a new Font object, we are ready to draw strings. To print a string in a graphics context, we should use method DrawString() of the Graphics object. This method is overloaded and has many flavors. Here we describe only some of them. The complete information can be found in the Visual Studio documentation.

The difference between this method and the previous one is that the string will not be shown outside of the assigned rectangle. Moreover, if the string is too long to fit the rectangle it can be printed in several lines (if the height of the rectangle is big enough). For example, see the result of the following code:
Drawing::Font *fnt = new Drawing::Font(S"Times New Roman", 0.5, FontStyle::Bold,
                                       GraphicsUnit::Inch);
gObj->DrawRectangle(pen, 10, 10, 50, 200);
gObj->DrawString(S"Hello", fnt, brush, RectangleF(10, 10, 50, 200));
 

Working with images

To draw images, Graphics class has two methods DrawImage() and DrawImageUnscaled(). These methods draw a given image or a portion of the image at the specified location. These are overloaded methods; that is, they can take different set of arguments, but all of them require the actual image to draw. This image argument is to be of the type Image.

The Image class is a basic class that provides functionality for Bitmap and Metafile descended classes. We will not discuss this class in details, just mention a couple of static method of the class. The first method, we would like to discuss is FromFile(). This method takes a String containing an image file name as a parameter and creates a new object of the Image class. For example, the cimplest code that displays an image stored in a file robot.gif will look like

Graphics *gObj = this->CreateGraphics();
Image *img = Image::FromFile( S"robot.gif" );
gObj->DrawImage(img, 5, 5);
The Image object also has several properties and methods we would like to mention:
Property/
Method
Description
Height Gets the height of this Image object.
Width Gets the width of this Image object.
Size Gets the width and height, in pixels, of this image.
HorizontalResolution Gets the horizontal resolution, in pixels-per-inch, for this Image object.
VerticalResolution Gets the vertical resolution, in pixels-per-inch, for this Image object.
RotateFlip(type) This method either rotates, flips, or rotates and flips the Image object. It takes only one argument - a RotateFlipType enumeration member that specifies the type of rotation and flip to apply to the image. The possible values of this type are:
Rotate180FlipNone Specifies a 180-degree rotation without flipping.
Rotate180FlipX Specifies a 180-degree rotation followed by a horizontal flip.
Rotate180FlipXY Specifies a 180-degree rotation followed by a horizontal and vertical flip.
Rotate180FlipY Specifies a 180-degree rotation followed by a vertical flip.
Rotate270FlipNone Specifies a 270-degree rotation without flipping.
Rotate270FlipX Specifies a 270-degree rotation followed by a horizontal flip.
Rotate270FlipXY Specifies a 270-degree rotation followed by a horizontal and vertical flip.
Rotate270FlipY Specifies a 270-degree rotation followed by a vertical flip.
Rotate90FlipNone Specifies a 90-degree rotation without flipping.
Rotate90FlipX Specifies a 90-degree rotation followed by a horizontal flip.
Rotate90FlipXY Specifies a 90-degree rotation followed by a horizontal and vertical flip
Rotate90FlipY Specifies a 90-degree rotation followed by a vertical flip.
RotateNoneFlipNone Specifies no rotation and no flipping.
RotateNoneFlipX Specifies no rotation followed by a horizontal flip.
RotateNoneFlipXY Specifies no rotation followed by a horizontal and vertical flip.
RotateNoneFlipY Specifies no rotation followed by a vertical flip.
Save( filename )
Save( filename, format )
Saves the image to the file assigned by the string argument filename. The second parameter specifies the format of the image file. For more information on the file format see the official documentation.

In the example above we have seen the simplest form of the DrawImage() method of the Graphics class. The easiest form of the method takes only three arguments - the image object to draw ant the coordinates of the top-left corner of the rectangle where the image should appear. Another version of the DrawImage() method takes the image to draw, the coordinates of the top-left corner, and the size of the rectangle to fit the image into:

DrawImage( Image *image, int x, int y, int width, int height);
For example, we can display the image in half of its original size:
gObj->DrawImage(img, 10, 5, img->Width/2, img->Height/2);
There is an interesting version of the method that allows us to fit the image not into a rectangle, but into a parallelogram. The syntax of the method is
DrawImage( Image *image, Point dest[] );
where the dest parameter specifies three points of a parallelogram. The three Point structures represent the upper-left, upper-right, and lower-left corners of the parallelogram. The fourth point is extrapolated from the first three to form a parallelogram. For example, to draw an image into a diamond we can do this:
Graphics *gObj = this->CreateGraphics();
Image *img = Image::FromFile( S"img/bluedrag.gif" );
Point dest[] = { Point(40, 0), Point(80, 40), Point(0, 40) };
gObj->DrawImage(img, dest);

Always use the Dispose() method for graphical objects like font, images, pens, brushes, etc when you are done using them Otherwise you are wasting Windows resources.

Jumping ball example

At the end let's create a relatively big example. We will develop a small program that will display a ball moving inside a rectangular field. Once a ball hits a boundary of the field it will keep moving in the reflected direction. First of all, open a new project and add a panel and a button to its form. Name the panel GpaphPanel and change the text on the button to "Start".

Second, add the following lines to the private section of the Form object definition:

Graphics   *gObj;
SolidBrush *brush;   // brush to display the ball
SolidBrush *erasor;  // brush to erase the ball
int         x, y,    // the current position of the ball
            dx, dy;  // the direction the ball is heading
Random     *rnd_gen; // we will generate all random numbers using this instance of the Random class

Now, click twice to the Start button to get into the button1_Click() method of the form. Here we need to create all the graphical objects and initialize the current position of the ball and its direction:

gObj = GraphPanel->CreateGraphics();
brush = new SolidBrush( Color::Red );
erasor = new SolidBrush( GraphPanel->BackColor );
rnd_gen = new Random( DateTime::Now.Ticks );

gObj->Clear( GraphPanel->BackColor );

x = rnd_gen->Next() % (GraphPanel->Width - 10);   // 10 - is the size of the ball
y = rnd_gen->Next() % (GraphPanel->Height - 10);
dx = 1 + rnd_gen->Next()%4;
dy = 1 + rnd_gen->Next()%2;

When the program starts, it will initialize all the needed variables and then it will repeat the same steps:

Since we would like to repeat these steps over and over again, we need a Timer object. Select the Timer on the Toolbox panel and click on your form. Name you timer NextStepTimer. Now, click on the Start button twice and add the line
NextStepTimer->Enabled = true;
to the button1_Click() method to start the timer.

Click twice on the timer and add the following code to the timer1_Tick() method:

// erase the ball
gObj->FillEllipse(erasor, x, y, 10, 10);

// change the X coordinates
x += dx;
if( x>GraphPanel->Width-10 || x<0 ){
    dx = -dx;
    x += 2*dx;
  // make the ball more transparent
    int alpha = brush->Color.A;
    brush->Color = Color::FromArgb(alpha>5?alpha-5:0, brush->Color);
}

// change the Y coordinates
y += dy;
if( y>GraphPanel->Height-10 || y<0 ){
    dy = -dy;
    y += 2*dy;
  // make the ball more transparent
    int alpha = brush->Color.A;
    brush->Color = Color::FromArgb(alpha>5?alpha-5:0, brush->Color);
}

// draw the ball in the new position 
gObj->FillEllipse(brush, x, y, 10, 10);
Enjoy.

Homework assignment

Read in Visual Studio documentation about the following classes and structures: