Tricks of the Windows Game Programming Gurus:Windows GDI, Controls, and Last-Minute Gift Ideas

All ITKnowledge Source Code Search Tips Advanced Search |

| Tricks of the Windows Game Programming Gurus by Andre LaMothe Sams, Macmillan Computer Publishing ISBN: 0672313618 Pub Date: 09/27/99

Search this book:

Previous | Table of Contents | Next

#### Getting a Line on Things

Now let’s draw the next most primitive complex—the line. To draw a line, we need to create the pen, and then make a call to the line-drawing function. Under GDI, lines are little more complex than that. GDI likes to draw lines in a three-step process:

  1. Create a pen and select it into the graphics device contexts. All lines will be drawn with this pen.
  2. Set the initial position of the line.
  3. Draw a line from the initial position to the destination position (the destination position becomes the initial position of the next segment).
  4. Go to step 3 and draw more segments if desired.

In essence, GDI has a little invisible cursor that tracks the current starting position of a line to be drawn. This position must be set by you if you want to draw a line, but once it’s set, GDI will update it with every segment you draw, facilitating drawing complex objects like polygons. The function to set the initial position of the line cursor is called `MoveToEx()`:

    BOOL MoveToEx(HDC hdc, // handle of device context

                  int X,   // x-coordinate of new current position

                  int Y,   // y-coordinate of new current position

                  LPPOINT lpPoint ); // address of old current position

Suppose you wanted to draw a line from (10,10) to (50,60). You would first make a call to `MoveToEx()` like this:

    // set current position

    MoveToEx(hdc, 10,10,NULL);

Notice the `NULL` for the last position parameter. If you wanted to save the last position, do this:

    POINT last_pos; // used to store last position

    // set current position, but save last

    MoveToEx(hdc, 10,10, &last_pos);

By the way, here’s a `POINT` structure again just in case you forgot:

    typedef struct tagPOINT

            { // pt

            LONG x;

            LONG y;

            } POINT;

Okay, once you have set the initial position of the line, you can draw a segment with a call to `LineTo()`:

    BOOL LineTo(HDC hdc,  // device context handle

                int xEnd, // destination x-coordinate

                int yEnd);// destination y-coordinate

As a complete example of drawing a line, here’s how you would draw a solid green line from (10,10) to (50,60):

    HWND hwnd; // assume this is valid

    // get the dc first

    HDC hdc = GetDc(hwnd);

    // create the green pen

    HPEN green_pen = CreatePen(PS_SOLID, 1, RGB(0,255,0));

    // select the pen into the context

    HPEN old_pen = SelectObject(hdc, green_pen);

    // draw the line

    MoveToEx(hdc, 10,10, NULL);

    LineTo(hdc,50,60);

    // restore old pen

    SelectObject(hdc, old_pen);

    // delete the green pen

    DeleteObject(green_pen);

    // release the dc

    ReleaseDC(hwnd, hdc);

If you wanted to draw a triangle with the vertices (20,10), (30,20), (10,20), here’s the line drawing code:

    // start the triangle

    MoveToEx(hdc, 20,10, NULL);

    // draw first leg

    LineTo(hdc,30,20);

    // draw second leg

    LineTo(hdc,10,20);

    // close it up

    LineTo(hdc,20,10);

You can see why using the `MoveToEx()—LineTo()` technique is useful.

As a working example of drawing lines, take a look at `DEMO4_2.CPP`. It draws randomly positioned lines at high speed. Its output is shown in Figure 4.4.

Figure 4.4 Line drawing program `DEMO4_2.EXE`.

#### Getting Rectangular

The next step up in the food chain of GDI is rectangles. Rectangles are drawn with both a pen and a brush (if the interior is filled). Therefore, rectangles are the most complex GDI primitives thus far. To draw a rectangle, use the `Rectangle()` function that follows:

    BOOL Rectangle(HDC hdc, // handle of device context

         int nLeftRect,     // x-coord. of bounding

                            // rectangle’s upper-left corner

         int nTopRect,      // y-coord. of bounding

                            // rectangle’s upper-left corner

         int nRightRect,    // x-coord. of bounding

                            // rectangle’s lower-right corner

         int nBottomRect);  // y-coord. of bounding

                            // rectangle’s lower-right corner

`Rectangle()` draws a rectangle with the current pen and brush as shown in Figure 4.5.

Figure 4.5 Using the `DrawRectangle()` function.

> Note: I want to bring a very important detail to your attention. The coordinates you send `Rectangle()` are for the bounding box of the rectangle. This means that if the line style is `NULL` and you have a solid rectangle, it will be 1 pixel smaller on all four sides.

There are also two other more specific functions to draw rectangles `FillRect()` and `FrameRect()`, shown here:

    int FillRect(HDC hDC,  // handle to device context

        CONST RECT *lprc,  // pointer to structure with rectangle

        HBRUSH hbr);       // handle to brush

    int FrameRect( HDC hDC,// handle to device context

        CONST RECT *lprc,  // pointer to rectangle coordinates

        HBRUSH hbr );      // handle to brush

`FillRect()` draws a filled rectangle without a border pen and includes the upper-left corner, but not the lower-right corner. Therefore, if you want a rectangle to fill in (10,10) to (20,20) you must send (10,10) to (21,21) in the `RECT` structure. `FrameRect()` on the other hand, just draws a hollow rectangle with a border. Surprisingly, `FrameRect()` uses a brush rather than a pen. Any ideas? In any case, here’s an example of drawing a solid filled rectangle with the `Rectangle()` function:

    // create the pen and brush

    HPEN blue_pen = CreatePen(PS_SOLID, 1, RGB(0,0,255));

    HBRUSH red_brush = CreateSolidBrush(RGB(255,0,0));

    // select the pen and brush into context

    SelectObject(blue_pen);

    SelectObject(red_brush);

    // draw the rectangle

    Rectangle(hdc, 10,10, 20,20);

    // do house keeping…

Here’s a similar example using the `FillRect()` function instead:

    // define rectangle

    RECT rect {10,10,20,20};

    // draw rectangle

    FillRect(hdc, &rect, CreateSolidBrush(RGB(255,0,0));

Previous | Table of Contents | Next

Copyright © Macmillan Computer Publishing, Inc.

Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.