Visual C++ Workshop
Session 3
R. Eckert

BITMAPS, ANIMATION, AND TIMERS

Bitmap: An Off-screen Canvas--

-Rectangular image that can be created with painting programs
-Data structure that stores a matrix of pixel values in memory
     -Pixel value stored determines color of corresponding pixel in image
-Windows supports 4-bit, 8-bit (indirect), 16/24-bit (direct) pixel values
-Can be stored as .bmp files (static resource data)
-Can be edited with paint programs
-Takes up lots of space
-GDI object that must be selected into a Device Context to be used
-Can be thought of as a canvas on a DC upon which drawing takes place
-Must be compatible with output device (video display or printer)
-Can be manipulated invisibly and apart from physical display device
-Can be transferred to/from physical a device-->flicker-free animation
-Doesn't store information on drawing commands (metafiles do that)
-Icons and cursors are small bitmaps

Using Bitmaps--

-Create and save a bitmap using a paint editor --> image.bmp file
-Add to program's resource script, e.g. in .rc file add the line:     
      IMG   BITMAP   image.bmp
-Load it from the program's resources:

      HBITMAP      hBitmap;
      HINSTANCE    hInstance;
      hInstance = (HINSTANCE)GetWindowLong (hWnd, GWL_HINSTANCE);
      hBitmap = LoadBitmap (hInstance, "IMG");

GetWindowLong()--
     One way of getting instance handle of app containing resources

LoadBitmap()--
     Gets bitmap from app's resourceshandle to bitmap
     One of many resource-loading functions

Steps in Displaying a bitmap--

   0. Get a DC with GetDC() or BeginPaint() as usual
   1. Create a memory device context with CreateCompatibleDC()
   2. Load bitmap with LoadBitmap() [or create w/ CreateCompatibleBitmap()]
   3. Select bitmap into the memory DC w/ SelectObject()
   4. Copy bitmap from memory DC to device DC w/ BitBlt() or StretchBit()

We can say:      Bitmap           Window Client Area
                ----------   =   --------------------
                Memory DC                DC

Memory DC-- 
     Like a DC for a physical device, but not tied to the device
     Used to access a bitmap rather than a device
     Bitmap must be selected into a memory DC before displayable on a device DC 

hMemDC=CreateCompatibleDC(hDC)-- 
     Creates a memory DC with same physical attributes as DC of given device

SelectObject(hMemDC,hBitmap) ;
     Selects bitmap into the memory DC 

BitBlt(hDestinationDC, x, y, w, h, hSrcDC, xsrc, ysrc, dwRop) --
     Copies pixels from bitmap selected into source DC to destination DC
     x,y -- coordinates of upper lefthand corner of destination rectangle
     w,h -- width, height in pixels of rectangle to be copied
     xsrc, ysrc -- coordinates of upper lefthand corner of the source bitmap
     dwRop -- raster operation for the copy


Raster Ops--

How to combine source pixel colors with current destination pixel colors
Bitwise Boolean operation (AND, NOT, OR, XOR, etc.)
Currently-selected brush pattern also can be combined:
256 different possible combinations (15 named):

Raster Op       Boolean Operation
Name                      S=source bitmap, D=destination bitmap
                P=currently-selected pattern (brush)
-----------------------------------------------------
BLACKNESS       0 (all black)
DSTINVERT       ~D
MERGECOPY       P & S
MERGEPAINT      ~S | D
NOTSRCCOPY      ~S
NOTSRCERASE     ~(S | D)
PATCOPY         P
PATINVERT       P ^ D
PATPAINT        (~S | P) | D
SRCAND          S & D
SRCCOPY         S
SRCERASE        S & ~D
SRCINVERT       S ^ D
SRCPAINT        S | D
WHITENESS       1 (all white)

StretchBlt(hDestinationDC,x,y,w,h,hSrcDC, xsrc,ysrc,wsrc,hsrc,RasterOp);
     Same as BitBlt() except size of copied bitmap can be changed
     Both source and destination width and height are specified

See BITMAP1 Example [uses BitBlt() and StretchBlt()]--



PatBlt(hDC, x, y, w, h, dwRop) -- 
     Paints a bit pattern on a specified DC
     Combination of currently-selected brush and pattern on destination DC
     x,y,w,h: position/size of rectangular area on destination DC
     dwRop (raster op): how pattern will be combined with destination pixels
          Possible values:
               BLACKNESS (0), DSTINVERT (~D),
               PATCOPY (P), PATINVERT (P^D), and WHITENESS (1)
     Pattern is tiled across specified area


BITMAP3 Example program-- 



"Blits" a bitmap using all 15 named raster ops:
     Source--white, red, yellow, blue, green bands: colors.bmp
     Destination--brick pattern of black and white tiles: brikbrsh.bmp
     Brush pattern--a blue-on-white diagonal hatched pattern
RasterOp names are displayed below each bitmap

A string table stores raster op names with program's resource data
     Alternative to defining  strings as static data in program
     Makes it easy to make changes

Defining a string table resource (.rc file)--

STRINGTABLE [load option] [memory options]
BEGIN
  idNumber   "Text String 1"
  idNumber   "Text String 2"
  ...
END

Can enter manually or use Developer Studio's String Table Editor

Loading a string from string table--
     LoadString(hInstance, idNumber, lpszBuf, cbSizeBuf);


ANIMATED GRAPHICS--

To create a moving picture--
     Want to give illusion of motion by continual draw/erase/redraw
     But how do we do it without taking over Windows?
     Shouldn't allow disruption of other applications

In a DOS application, we could do the following:

while (TRUE)
{
  /* exit loop if a key is pressed */

  /* erase old object image */
  /* compute new location of object */
  /* draw object at new location */
}

In Windows:
     Other programs can't run while this loop is executing
     Need to keep giving control back to Windows so other programs can operate
     Especially critical under Windows 3.xx (cooperative multitasking)

One method: Use a PeekMessage() loop instead of a GetMessage() loop.

GetMessage() only returns control if a message is waiting for calling program

PeekMessage() returns (with 0 value) if no active messages are in the system

PeekMessage() loop can take action (redraw image) if PeekMessage() returns 0

PeekMessage() doesn't return zero for WM_QUIT (like GetMessage())
     App must explicitly check for a WM_QUIT message to exit the program

PeekMessage(lpMsg, hWnd, uFilterFirst, uFilterLast, wRemove);
   -The first 4 parameters are same as GetMessage.
   -Last one: specifies whether message should be removed from the Queue.

PeekMessage() message loop will look like:

while (TRUE)
{
  if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
     {                           // non-zero means we must handle msg
     if (msg.message == WM_QUIT)
        return msg.wParam;       // return to Windows if msg is WM_QUIT
     else
        {
        TranslateMessage (&msg);
        DispatchMessage (&msg);  // dispatch msg to our WndProc()
        }
     }
  else                           // zero  dead time that can be used
     {
     do some other stuff like drawing the next animation frame
     }
 }


BALL Example Animation Application (Ball bouncing off walls)--



BALL.H--

   Define menu item constants
   Define ball constants: (VELOCITY, BALLRAD, MINRAD)

BALL.CPP--

Global variables--
   _dDrawOn: on/off switch to toggle animation on or off
   _nXSize,_nYSize: window width, height--to determine if ball is inside
       (if window is resized, we need to change these)

New Messages--

WM_CREATE:
     When window is created (not yet visible)

WM_SIZE message: 
     Sent anytime window is resized by the user
     Vertical/horizontal size of client area encoded in lParam
         -Least significant two bytes = horizontal size in pixels
         -Most significant two bytes = vertical size

Helper function DrawBall()--
     Draws ball in new position for each new frame


DRAWING ON A MEMORY BITMAP (Improving an Animation)

Flicker-
     Result of multiple accesses to frame buffer in each frame
     Avoid by making one access to frame buffer during each new frame
     In Windows do this with off-screen memory bitmaps
          Use GDI to “draw” invisibly on a bitmap selected into a memory DC
          When done, BitBlt()  result to device DC
               Very fast & one access to frame buffer
                     No flicker in animation applications

Getting a blank Bitmap to draw on-
     (Alternative to LoadBitmap())

     hBitmap = CreateCompatibleBitmap (hDC, w, h);
          hDC--handle to device context bitmap is to be compatible with
          w,h--width and height of bitmap

Select into a memory DC & use GDI graphics functions to draw on it 
     All GDI drawing operations now invisible to user

When drawing is all done, BitBlt() it to real device

Animation of a moving object over a stationary background--

Set up an offscreen bitmap and select it into a memory DC

For Each Frame (each time PeekMessage() returns):
     Calculate new position of the object(s)
     Erase entire off-screen bitmap (or BitBlt() background bitmap to it)
     Redraw object(s) (in new positions) on the off-screen bitmap
     BitBlt() entire off-screen bitmap to the screen




See the BALLBLT example program--



But for large image field, BitBlt() could cover a large area==>slow
     
Better method: 
     Compute affected area (rectangle enclosing old & new object position)       
     BitBlt() to that area only


Sprite-
     A little bitmap that moves around on screen
     Could restore background and BitBlt() the over it

Problem-
     Sprite consists of desired object enclosed in rectangle
     Rectangular shape
     “Halo” around sprite  

Solution (Sprite Animation)--

1. Set up a "mask bitmap":
     Sprite pixels black, rest of enclosing rectangle white

2. BitBlt() this over background using SRCAND (AND) raster op

3. Set up an "image bitmap": 
     Sprite pixels set to their colors, rest of enclosing rectangle black

4. BitBlt() this to result of step 2 using SRCINVERT (XOR) raster op

Result: sprite moves to new location with surrounding background intact




DEVICE INDEPENDENT BITMAPS (DIB)--

Device Dependent Bitmaps (DDB)-
     Type of bitmap considered until now
     Same color organization as output device
     Only dimensions of display and the pixel values stored
     No information on how pixel values are mapped to actual RGB colors
          Can have color fidelity loss on device with different color scheme 

Device Independent Bitmap (DIB)-
     Introduced in Windows 3.0
     Includes its own color table 
          Shows how pixel values map to RGB colors 
     Can be displayed on any raster output device
     Color mapping info in DIB used to convert to nearest device colors

Using a DIB--

Can't be selected into a DC==>no BitBlt()
How do we display it?

1. Convert to a DDB using CreateDIBitmap();
     Creates a DDB and returns a handle to it
          Can be used as any other DDB (BitBlt(), etc.)
     Memory intensive--requires twice the memory

 or

 2. Use StretchDIBits();
      Copies bits directly from a DIB to target DC
      Used like StretchBlt(), but with differences (search "StretchDIBits")


MICROSOFT’S DIRECT-X GRAPHICS LIBRARY--

BitBlt() and StretchBlt() are relatively fast
But can’t match direct accesses to frame buffer
Prohibited under standard Windows
     Best fast-action game programs had to be written as DOS apps
Microsoft’s Response: Direct-X graphics library--
     Permit direct access to the frame buffer… 
     and to acceleration hardware built into the system's video card

Beyond scope of this workshop
   Refer to the references
   and my CS-360 notes on DirectX and Windows Game Programming"
      (CS-360 link off my home page)
  
THE WINDOWS TIMER--

An input device that notifies an app when a time interval has elapsed 
    Application tells Windows the interval 
    Windows sends WM_TIMER message each time interval elapses

Some Timer Applications--

Keeping time (clock programs)-
     Not precise
Waking up
Multitasking-
     Divide job up into smaller pieces
     Process each piece on receipt of timer message
Maintaining an updated status report
Autosave feature
Pacing movement-
     Timer messages can trigger next animation frame
     No inconsistencies from variations in processor speed
Activation of a screen saver after a certain period of time
Terminating demonstration versions of programs
     Timer message signals when time is up
Multimedia-
     Use a timer to coordinate audio/visual information

Using a Timer--

Allocate and set a timer with:

  SetTimer(hWnd, timerID, Timeout, f_address);

Parameters:
     hWnd of window to receive timer message
          If last parameter is NULL, window's WndProc() receives msgs
     Timer id (UINT);
     Timeout duration (UINT): msec interval between WM_TIMER msgs
     TimerProc: address of procedure that will receive/process timer msgs
          (a "callback" function)

Possible Return Values (UINT):
     Identifier of new timer if 2nd argument was NULL & call was OK
     Zero if unsuccessful

From then on, system repeatedly generates WM_TIMER messages
Timer resets itself each time it times out

WM_TIMER message:  wParam = Timer ID, lParam = 0.

KillTimer(hWnd, timerID);
     Stops timer messages and removes timer from system
     App should call this when done using timer
     Make sure all timers are killed prior to program termination

Checking if timer was allocated successfully-
     Finite number of timers available
     If none available, app could put up a Message Box 
          Advise user to close other apps that may be using timers:

while (!SetTimer(hWnd, 1, 1000, NULL))
  if (IDCANCEL == 
       MessageBox(hwnd, 
                  "Too many timers!", 
                  "Program Name",
                  MB_ICONEXCLAMATION|MB_RETRYCANCEL) )
       return FALSE; /* return if user hits “Cancel” */ 

How does a timer work?
     -Windows redirects PC’s 8253/54 hardware timer interrupt (INT 8)
          Occurs every 54.925 msec. (18.2 times a second)
     -Windows handler decrements counters for all set timers 
     -When counter reaches 0, WM_TIMER msgapp's queue & timer reset
          Resolution is that of PC timer (18.2 times per second)
          WM_TIMER messages can't be generated any faster

WM_TIMER messages are low priority (like WM_PAINT) 
     If other apps are busy, pgm may not get WM_TIMER msgs at right time       
     Put on pgm's queue, but until pgm regains control, it won't receive them

WM_TIMER Message may not be received at all-
     Moving/resizing window "modal message loop":
          Windows prevents interference 
          Traps messages with internal loop 
                Discarded and don't make it to the pgm's message loop
     
So, don’t use timers for precision timekeeping!

BEEPER1 Example (Petzold)--Beeps and changes colors every second
     Resize/MoveBeeping stops!

Timers in Animation-
    Use as alternative to PeekMessage() loop
    Timer message triggers generation of next frame
         Animation will run at same speed on all machines


BALLTIME Example (like BALL; uses Timer instead of PeekMessage())--

Callbacks: Alternative Way of Using Timer-

Callback Function--
     Function in our program called by Windows
     Give Windows info on location of callback function
     Windows calls it when required:
          For timer callback, when WM_TIMER message received
     Sort of like the WndProc()
     Must be of type CALLBACK
     Parameters & return value depend on purpose of callback 
     Also used with dialog boxes
     For timer callback, parameters are same as for the WndProc():

VOID CALLBACK TimerProc(HWND hWnd, UINT message,
                         WPARAM wParam, LPARAM lParam);

     HWnd: handle to window specified when SetTimer() called
     Message: always WM_TIMER
     wParam: timer ID
     lParam: current system time

If used, it should do processing of WM_TIMER messages

Must tell Windows where this function is:
     Do so in 4th parameter of SetTimer()
          Just the address (name) of the timer callback function
          Under Windows 3.xx, it was much more complicated

Example: Set timer #2 for 50 msecs & use the callback TimerProc()--
     SetTimer (hWnd,2, 50, (TIMERPROC) TimerProc);

BEEPER2 (Petzold)--An example of using a timer callback function

PERFORMANCE MONITOR COUNTERS-

     High-resolution timers 
     Provide most accurate time measurement possible in system
     Used in apps where precise time measurement is important, e.g.:
          Scientific measurements
          Musical interval timings
          Game interactions
          Performance monitoring of code execution

QueryPerformanceCounter(*pmilliseconds)-
     Current performance counter value returned in *pmilliseconds, 64-bit precision
     Returns FALSE if system has a high-resolution timer, TRUE if not
     See online help for details

Getting Absolute Time Information-
     Use C library functions time() and localtime()
     MFC provided CTime class
     See on-line help for details