This blog entry is a lead in to a new series of articles about developing GUIs with the .NET Micro Framework.
A good place to start when learning a new GUI framework is to learn how to draw simple graphics. This blog entry discusses how to draw simple graphics with the .NET Micro Framework.
Creating a Bitmap
The Microsoft.SPOT namespace contains a Bitmap class which represents a bitmap image. To create a bitmap the same size as your physical screen you could use a code snippet such as the following:
using Microsoft.SPOT; using Microsoft.SPOT.Presentation; Bitmap bmp = new Bitmap(SystemMetrics.ScreenWidth, SystemMetrics.ScreenHeight);
Once you have a bitmap you can draw on it by using the various instance methods of the Bitmap class. When your drawing is completed, you need to copy the bitmap to the LCD screen in order for it to become visible. The framework provides a Flush() method to perform this task. Calling Flush() on your bitmap will copy the bitmap data to the LCD screen.
Bitmap bmp = new Bitmap(SystemMetrics.ScreenWidth, SystemMetrics.ScreenHeight); // ... do drawing stuff here ... bmp.Flush(); // copy bitmap to LCD
It is important to note that to use the Flush() method your bitmap must be exactly the same size as the LCD display. Otherwise the flush will simply not work, even though no exception or debug diagnostic will indicate a problem while debugging. This is a common trend with many of the .NET Micro Framework Base Class Library methods.
This enumeration only has the values Black and White pre-defined. For example to specify the colour White you could use a code snippet such as the following:
using Microsoft.SPOT.Presentation.Media; Color white = Color.White;
It is possible to specify other colours by specifying the red, green and blue intensity values that make up the desired colour. To do this you use a static method within the ColorUtility class called ColorFromRGB as shown below:
using Microsoft.SPOT.Presentation.Media; // Specify full intensity red Color red = ColorUtility.ColorFromRGB(255, 0, 0);
The parameters passed to ColorFromRGB are the Red, Green and Blue components of the desired colour. These values are all bytes which range from 0 to 255 (full brightness).
ColorFromRGB basically encapsulates some simple bit shifts and a typecast. Internally the .NET Micro Framework represents colours as 3 8bit fields packed into a single 32bit unsigned integer. Instead of using the ColorFromRGB method we can perform a manual typecast between a suitable number and the Color enumeration as follows:
using Microsoft.SPOT.Presentation.Media; // 0xBBGGRR Color red = (Color)0x0000FF;
The format when the value is expressed in hexadecimal is 0xBBGGRR, i.e. 8 bits red (R), 8 bits green (G), followed by 8 bits blue (B). So the above example creates a red colour with full intensity.
The bitmap class has numerous methods available for drawing the outlines of basic shapes such as lines, rectangles and ellipses.
- Drawing lines:
// Draw a red line 10 pixels thick // between (x=20, y=30) and (x=40, y=50). bmp.DrawLine(red, // colour 10, // thickness 20, // x0 30, // y0 40, // x1 50) // y1;
A line is specified by providing the colour, thickness and start and end co-ordinates of the line. The current implementation of the .NET Micro Framework base class library appears to ignore the thickness parameter, all lines are drawn 1 pixel wide.
- Drawing rectangles:
// Draw a rectangle which is 40 pixels // wide and 50 pixels high. The top left // corner is at (x=20, y=30). The outline is // 10 pixels wide in red. bmp.DrawRectangle(red, // outline colour 10, // outline thickness 20, // x 30, // y 40, // width 50, // height 0, // xCornerRadius, 0, // yCornerRadius, 0, 0, 0, 0, 0, 0, 0);
Drawing a rectangle involves using the DrawRectangle method which potentially requires setting a number of parameters. We will initially ignore the last 7 parameters and set them to zero (we will discuss them later when we cover gradient fills.).
If the outline thickness is greater than 1 then the co-ordinates specified indicate the center of the outline, i.e. half the outline is drawn on each side.
Rectangles with rounded corners can be specified by setting the xCornerRadius and yCornerRadius parameters to the desired radius. If the radius is larger than zero the outline thickness is ignored by the current version of the BCL and the framework reverts to drawing a 1 pixel thick outline.
- Drawing Ellipses:
// Draw an ellipse centred at (x=30, y=60) // with a radius of 10 on the x axis and // 20 on the y axis. bmp.DrawEllipse(red, // colour 30, // x 60, // y 10, // x radius 20); // y radius
The simplest way to draw an ellipse is to specify the colour, center co-ordinates, and then the radiuses for the x and y axis respectively. This allows drawing not only ellipses, but also circles (which simply have the x and y radiuses the same).
There is a more complex overload of the DrawEllipse method which enables you to specify the thickness of the outline and/or fill the inside of the shape. However both features are not implemented by the current version of the base class library.
DrawEllipse and DrawRectangle both have overloads that support specifying a gradient fill to colour in the internal area of the shape (the 7 parameters set to 0 in the above examples).
The specification of a gradient fill consists of a start and end co-ordinate and associated colours at those two points. The framework will then apply a linear gradient between those two points. Any point “before” the start co-ordinate will be the starting colour, while any point “after” the end point will be the end colour. If both the start and end colours are the same a solid fill will be obtained.
The co-ordinates for the gradient start and end points are measured in screen co-ordinates. I.e. they are relative to the top left corner of the LCD and could refer to locations outside the area of the shape being drawn. This fact can be used to produce some interesting rendering and animation effects.
The opacity parameter allows the fill to be semitransparent and show previous content drawn to the same region of the bitmap. The opacity is a byte value with 0 indicating fully transparent, and 255 indicating full opaque (solid fill).
The fill effect shown in the image above was achieved via the following code sample. Notice the direction of the linear fill (as dictated by it’s start and end co-ordinates), and the fact that the bottom right half of the rectangle is a solid white fill due to this region being “after” the gradient’s end point.
bmp.DrawRectangle(Color.White, // outline colour 0, // outline thickness (no outline) 50, // x 50, // y 100, // width 100, // height 0, // x corner radius 0, // y corner radius red, // start gradient colour 50, // start gradient x 50, // start gradient y Color.White, // end gradient colour 100, // end gradient x 100, // end gradient y 0xFF); // opacity of fill
The sample application available for download demonstrates a number of basic drawing operations as discussed above. The application cycles through a number of demonstrations. The sample application also demonstrates the use of System.Reflection functionality within the .NET Micro Framework to find the examples. If you would like to experiment with the drawing APIs, this sample application would be an ideal test harness, just add another “Example_XYZ” method that contains your drawing code and your example will be automatically picked up.
Another sample application is available for download (without explanation as to how it is implemented). This example helps demonstrates the rendering capabilities of the .NET Micro Framework by creating and animating up to 50 random rectangles of different size, colour and alpha transparency over top of the .NET Micro Framework snowflake logo. It also demonstrates the fact that the .NET Micro Framework emulator is really a simulator. You will notice that running this example under the emulator produces very impressive rendering speeds which are not matched when running on actual hardware.
My next blog entry about the .NET Micro Framework will discuss how to create a basic WPF style application. Eventually I will outline an alternative approach for drawing basic shapes that enables the WPF framework to take care of compositing the individual shapes onto the screen, enabling basic shapes to be animated and moved around in a more object orientated manor.