Visual Basic provides a few methods for drawing on Forms and PictureBoxes, which are discussed at length in Chapter 6. There are only a few drawing methods, hut they accept a variety of arguments and they are quite flexible. However, they are not as fast as their API counterparts (the functions a C++ program would call); Windows is a ‘Visually rich environment and it provides a number of API functions for drawing and painting. In this section, I will discuss a few of the API drawing functions that will help you speed up your Visual Basic application (a discussion of all related API functions is beyond the scope of this book). In the last section of the chapter, I will present a few basic concepts of the Windows API drawing functions, device contexts, and bitmap structures, These techniques will help you write very fast code for drawing and pixel manipulation.
Drawing Lines and Circles
The two basic API drawing functions are the LineTo() and Ellipse() functions which draw lines and ellipses/ circles, respectively. The LineTo() function’s syntax is:
Public Declare Function LineTo Lib “gdi32” Ali,as “LineTo” _
(ByVal hdc As Long, ByVal x A~ Long, ByVal y As Long) As Long
The LineTo() function draws a line from the current point to the coordinates X, Y on the specified device context. To set the current point, use the MoceToEx() function, whose declaration is:
The.arguments x and y of the MoveToEx() function are the coordinates of the new . current point. The last argument is a POINTAPI structure, and it holds the coordinates of the current point before it was changed by the function (in other words, the coordinates of the Current point before the function changed it). The POINTAPI structure Was explained previously in the section “Declaring32 -Bit Functions and Structures. “
The coordinates for both functions (and all the API drawing functions) must be expressed in pixels. If you’re going to mix Visual Basic drawing methods and API drawing functions, you should set the coordinate system of the respective control(s) to 3 (Pixels),. To draw a line from the point (10, 10) to the point (85, 130) on Form1, use the following statements:
Di. point As POINTAPI
MoveToEx Form1.hDC, 10, 10, point
LineTo Form1, hDC., 85, 130
To draw circles and ellipses, use the Ellipse() function, whose syntax is:
The Ellipse() function draws an ellipse on the hDc device context, The ellipse is enclOsed by the rectangle defined by the coordinates (X1, Y1)and (X2, Y2). To draw . a square, make the sides of the rectangle equal.
If you use these commands to draw on a Form or PictureBox control, the shapes will be drawn in the control’s drawing color (property ForeColor) and with the control’s drawing width (property DrawWidth). You can change these settings using the properties of the Form or control or you can use the Createpen() API function as follows:
The nPenStyle argument is an integer, which can take any of the values shown in Table 13.8. These values correspond to the settings of the Draw Mode property.
myPen = CreatePen(0, 2, RGB(255, 0, 0))
Creating a Pen object doesn’t mean that it will be automatically used by subsequent drawing operations. To use the new pen, you must register the Pen object with the device context to which it applies. To do so, you must call the Select- Object() function, whose syntax is:
Public Declare Function SelectObject Lib ‘gdi32″ Alias ‘SelectObject’
(ByVal hdc As Long, ByVal hObject As long) As long
The first argument is the handle of the device context where the drawing will take place, and the second argument is the handle of the Pen object. To specify that the myPen object be used in subsequent operations, you must call the following function:
SelectObject Form1.hDC, myPen
After this statement is executed, the Line() and Ellipse() functions will use the myPen pen to draw on the device context. Analogous to the Pen object is the Brush object, which is used for filling areas (you’ll see how it is done later). The simplest function for creating a Brush object is the Createsolidbrush() function, whose syntax is:
Public Declare function CreateSolidBrush Lib ‘gdi32’ Alias
‘CreateSolidBrush’ (ByVal crColor As Long) As Long
crColor is a Long Color value. You can use the RGB() function to specify the brush’s color or use the Color common dialog box’s Color property. To use the Brush object . with subsequent operations, you must select it into the device context with the SelectObject() function.
VB6 At Work: The APIDraw Project
The APIDraw project demonstrates the API drawing functions discussed in the previous section. It’s a simple application that draws a circle and an ellipse and-their bounding rectangles, as shown in Figure 13.11.In the next section, you’ll see how to fill the various closed sections of the drawing.
The following API function declarations appear at the beginning of the code:
Filling Closed Shapes
A very useful drawing feature missing from Visual Basic is a method that fills a closed area. You can draw filled circles and rectangles, but what about shapes that are defined as intersections of other basic shapes, like the filled areas of the drawing shown in Figure 13.11? To fill irregular shapes, you must use the ExtFloodFill() function, whose declaration is:
This function fills a solid area starting with the point at coordinates (x, y) with the color specified by the argument crColor. The last argument, wFillTyPe, specifies the type of filling and it can have one the following values:
- FLOODFILLBORDER (0) The function-fills the area colored with crColor. The point (x, y) must lie in the area to be filled.
- FLOODFILLSURFACE (1) The cIColor argument specifies the bounding color.
The area is filled with a brush. so you must first create a Brush object; select it into the device context, and then call the ExtFloodFilI() function. The drawing produced by the Draw Now button of the Ellipse application contains interesting areas to fill with the ExtFloodFilI() function, so we’ll add some code to’ this application to fill closed areas with a color selected by the user on a Color common dialog box. To fill an area, click the Fill Color button (which will display the Color common dialog box, where you can select the fill color) and then click a point In the area you want to fill. The filling takes place from within the MouseUp event with the following statements.
Filling a closed area
The code extracts the selected color from the CommonDialog1 control and uses it create a solid brush. The brush object is selected into the Form’s device context and used by the ExtFloodFill() function to fill the area.
Pixel Painting Functions
I can’t think of many applications that spend more than a few milliseconds drawing lines, except-for a few really elaborate math curves, so it’s probably not worth the effort to use the API functions to draw shapes. But as you may recall from Chapter 7, the Image application had to read and set the values of nearly one million pixels during image processing. In this instance, every millisecond you can squeeze out of the . processing cycle of a single pixel really adds up. In Chapter 12, Optimizing’VB Applications, you learned about the GetPixelV() and SetPixelV() functions, which are significantly faster than their Visual Basic counterparts (Point and PSet methods) in reading and setting a pixel value, respectively.
The GetPixelV() function returns the value of the pixel at coordinates (x, y) on the device context specified by the argument hDc as follows.
Figure 13.12 shows the CopyPix application, which copies the pixels of the top PictureBox to the bottom one using either straight Visual Basic code (the Copy VB button), or the GetPixelV() and SetPixelV() API functions (the Copy API button). Run the project to see how much faster the API functions are compared with the equivalent Visual Basic methods. You don’t even have to time the two operations; you can “see” the difference in execution speed. Notice that the AutoRedraw property of the second PictureBox is set to False, so that you can watch the progress of the pixel copying, operation .
The CopyPix project should be executed on system that can-display more than 256 colors. If your system supports 256 colors only, make sure that the color you select to fill an area is solid. If it’s a dithered color, the area will be filled with a pattern of different colored dots. If you attempt to fill this area again, the code will most likely fill a single pixel. The ExtFloodFill() function fillS an area of solid color only and will fail with dithered colors.
The application’s code is straightforward, so i will only list the code of the Copy API button. The program scans every pixel in the source image with a double loop. reads its value, and then assigns it to the pixel at the same coordinates in the lower PictureBox. The actual code is shown next.
Copying Pixels with API Functions
To copy images between controls you must use the PaintPicture method or the BitBlt() API function; they are both nearly instantaneous. If you have to process pixels, however, the two API functions discussed in this section will speed up the application considerably. In Chapter 12, we used-the pixel manipulation functions to optimize the Image application, which spends most of its time reading and setting pixel values ..