<-- [prev] . [next] -->
Quick introduction to Windows API

7. Graphics

My main interest in graphics is in plotting functions and graphs, and educational games. Therefore the following introduction concentrates on line-plotting rather than bitmaps.

Once you have a window, the basic line plotting functions are MoveToEx and LineTo. However, to use these, first you have to get a device context (DC) and select a pen. You can learn the main ideas by inspecting my "EZplot" function. You pass to the function a window handle and a pair of valarrays; it plots the x,y curve in the window. (If you don't like valarray then just change the types to standard libarary vector, this will work equally well here).

Here is the first part of the EZplot function:

bool EZplot(HWND hwnd, const valarray& x, const valarray& y)
   RECT rect;
   GetClientRect(hwnd, &rect);
   HDC hdc = GetDC(hwnd);
   if (hdc)
      HPEN pen = CreatePen( PS_SOLID, 0, RGB(0,0,0) );
      if (pen==0) disp("EZplot failed to create pen");
      HPEN prevpen = (HPEN) SelectObject(hdc, pen);
      if (prevpen==0) disp("EZplot failed to select pen");
This illustrates the following sequence which is typical of graphics functions in windows API:
  1. Get a pointer to a device context, and check to make sure you got a valid one.
  2. Create an object such as a pen, and check to make sure that Windows created it succesfully.
  3. Tell the device context to use the pen you just created, and receive a handle to the pen it has been using up till now. Check to make sure the device context is happy with your new pen.
Note, there are 3 steps and each involves a check! Don't take the illusory short-cut of omitting the checks: I have found that Windows graphics functions can give surprising behaviour (i.e. fail for no obvious reason) and you will need all the checks you can get. The SelectObject function returns a handle to the object that the DC has been using up till now. For friendly behaviour, you should store this information, then select this "previous pen" back into the DC when you are done: see the "tidy up" section at the end of EZplot.

EZplot also shows you how a create and select a brush. A pen is for drawing lines, a brush is for filling rectangular regions with colour. To give your window some chosen colour, you need to declare a RECT structure and call GetClientRect, then call Rectangle to do the painting:

GetClientRect(hwnd, &rect);

// draw rectangle with current pen, and fill it with current brush
Rectangle(hdc, rect.left,rect.top,rect.right,rect.bottom);

MovetoEx and LineTo can be used to plot a curve as follows:

MoveToEx(hdc, (int) ix[0], (int) iy[0], (LPPOINT) NULL);
      for(int i=1; i < x.size(); ++i) LineTo(hdc, (int) ix[i], (int) iy[i]);
Of course, if you are dealing with integers not real values then you would use a valarray or vector (or array of int) and then you don't need the (int) type-casting.

You now have all the information you need to do quite a lot of graphics stuff. However, in practice you need to get organised: you will soon want to juggle several pens and regions of the window, or several windows, and then you will find it useful to define a handful of classes, possibly with a heirarchical structure, to handle all your graphics with the right amount of flexibility. Therefore you should regard "EZplot" as a useful debugging tool, but it is not the way to do most of your graphics.

My own approach goes something like this. Basic classes with names such as "MyPen" and "Canvas" provide the connection to the Windows API. Calls to Windows (i.e. non-user-defined) functions are ONLY to be made via these classes. Other classes such as "Frame", "Axis", and "Plotline" receive things like lines to be plotted or line types or axis limits, and call the relevant low-level routines. In my structure, a "Canvas" holds a vector of "Frames", and each "Frame" holds two "Axis" and a vector of "Plotlines". You can think of a Canvas as a window, a Frame as a rectangular region within the window, and a Plotline as a plotted line. Each Plotline knows which Frame it is in, and each Frame knows which Canvas it is in. This enables a Plotline to plot itself (by asking the Frame to do it), and each Frame can redraw itself (by asking the Canvas to do it).

If you look up Look up "lines and curves" at msdn/msdn library (in Jan 2009 this was at http://msdn.microsoft.com/en-us/library/ms534260(VS.85).aspx) you get the following list

AngleArc Draws a line segment and an arc..
Arc Draws an elliptical arc.
ArcTo Draws an elliptical arc.
GetArcDirection Retrieves the current arc direction for the specified device context.
LineDDA Determines which pixels should be highlighted for a line defined by the specified starting and ending points.
LineDDAProc An application-defined callback function used with the LineDDA function.
LineTo Draws a line from the current position up to, but not including, the specified point.
MoveToEx Updates the current position to the specified point and optionally returns the previous position.
PolyBezier Draws one or more Bézier curves.
PolyBezierTo Draws one or more Bézier curves.
PolyDraw Draws a set of line segments and Bézier curves.
Polyline Draws a series of line segments by connecting the points in the specified array.
PolylineTo Draws one or more straight lines.
PolyPolyline Draws multiple series of connected line segments.
SetArcDirection Sets the drawing direction to be used for arc and rectangle functions.

For a long list of GDI functions, including things like dealing with bitmaps, see MSDN->MSDN Library->Mobile and Embedded Development-> Shared Windows Mobile 6 and Windows...->Graphics, Windowing and Events (GWE...-> GWES Reference->GDI Reference->GDI Functions. The next page reproduces this list (as on January 2009).