Drawing with GDI
|This article is based on Visual Basic 6. Find other Visual Basic 6 articles.|
In the days of DOS, in order to draw to the screen, one would have to deal with the graphics memory directly, sometimes using special methods for different graphics chipsets, like an over-complex version of the more modern DirectX or OpenGL. When Windows 95™ came out, it introduced a new layer of abstraction invented by Microsoft called the Graphical Device Interface (GDI). With it, it is possible to easily draw just about anything with sufficient speed for user interfaces.
There are several fundamental interfaces in GDI that need to be considered:
- Bitmaps - The underlying interfaces that store raw image data
- Device Contexts - Data structures to which the GDI functions 'draw'
- Brushes - Inside fill colours
- Pens - Outline colours
- Fonts - Data structures containing information about how text is to be rendered
However, as this is just a simple introductory tutorial, we will deal only with device contexts, brushes and pens.
Note: All of the GDI functions used here are functions that you must declare, and are not intrinsic to VB. These declarations can be found in the API Viewer tool that comes with VB.
GDI Objects are not objects in the sense that you might be used to objects in VB. Since GDI is a library that was written in C, all of the 'objects' that we use are in fact referenced by a number, that we call the object's handle. When we create an object, the function with which we create the object returns to us the handle of the object. We then pass this handle to the different GDI functions in order to manipulate and utilise it throughout its lifetime. The object can be selected into and out of device contexts (don't worry about what this means just yet), and then used in conjunction with other functions to draw with. When we are finished with the object, we delete it, using the DeleteObject() function. This cleans up resources and prevents us gathering memory leaks.
Device contexts (DCs) are structures that define a set of graphic objects and their associated attributes, and graphic modes that affect output. In GDI it is said that we are drawing to the DC. What is really happening behind the scenes is that GDI is modifying some bitmap data in memory, but for the purposes of this tutorial, that is not something that we need concern ourselves with.
As aforementioned, you can select objects in and out of a DC. When you draw to a DC, many functions will use the object, e.g. the pen, brush or font, selected into the device context to draw with. For example, if you wanted to draw a red line on a DC, you would first have to create a red pen, then select it into a DC, then draw to the DC using that pen, then select the pen out of the DC and destroy it, and such is the lifeline of a GDI object.
You can also create and destroy DCs and Bitmaps, but that is more advanced and not covered in this tutorial. Here we will only talk about drawing to a control/form's DC, accessed in VB by its hDC property (if it has one - note this means you can not draw to label controls).
Note: You MUST select an object out of all DCs into which it is selected before destroying it.
There are several types of brushes in GDI, but for the time being we are concerned only with solid brushes.
Creating a Solid Brush
In order to create a solid brush, we call the CreateSolidBrush API (duh!), which accepts one argument - the colour:
Dim hBrushRed As Long '-- Note: The naming convention for GDI objects is to put a 'h' before ' the object type name, and then the variable name that you wish ' to call your object (e.g. the colour of it in this case) hBrushRed = CreateSolidBrush(vbRed)
Drawing with our brush
For our example, we will draw using the function Rectangle, which draws a rectangle filled with the DC's current brush, and outlined with its current pen. Rectangle accepts 5 arguments: the hDC to draw to, plus a four variables that represent the upper left and lower right co-ordinates of the rectangle to be drawn (in logical device units - in this case pixels). In order to actually draw with the brush we created, we add an extra little step into the process:
Dim hBrushRed As Long hBrushRed = SelectObject(Me.hDC, CreateSolidBrush(vbRed)) Rectangle Me.hDC, 10, 10, 100, 100 DeleteObject SelectObject(Me.hDC, hBrushRed)
What did we do here? Well we selected the created brush into the form's DC just after it was created. When we make a call to SelectObject, it returns the handle to the object that was previously selected into the DC (in this case it's a brush). This handle is then stored in hBrushRed instead of our actual handle. The reason for doing so is that, when you want to destroy the object, as can be seen in the last line, you can select the old object back into the DC, returning our handle and passing it to DeleteObject, returning everything neatly to normal.
Like brushes, there are also several kinds of pens in GDI, and once again, we will deal only with solid pens in this tutorial.
Creating a Solid Pen
Creating a pen works slightly differently to creating a brush, in that, as well as specifying the colour of the pen, you have to specify both its width and style as well. The styles of pens are defined in constants in the form of PS_*. These constants can be found with the API Viewer tool that comes with VB. We will use PS_SOLID here, whose value is 0:
Private Const PS_SOLID As Long = 0 Private Sub Form_Paint() Dim hBrushRed As Long Dim hPenBlue As Long hBrushRed = SelectObject(Me.hDC, CreateSolidBrush(vbRed)) '-- The arguments of CreatePen are: Pen Style, Pen Width, Pen Colour hPenBlue = SelectObject(Me.hDC, CreatePen(PS_SOLID, 3, vbBlue)) Rectangle Me.hDC, 10, 10, 100, 100 DeleteObject SelectObject(Me.hDC, hPenBlue) DeleteObject SelectObject(Me.hDC, hBrushRed) End Sub
Note that I have now declared PS_SOLID as a constant outside the sub in the declarations section, and that I have put the rectangle drawing code in the form's Paint event, meaning that the rectangle will be repainted every time the form is.
And that's all there is to it! Of course this tutorial doesn't even begin to scratch the surface of what is possible with GDI, but it gives you a good idea of some of the basics of its use.