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