This section describes changes you could make to an application's main window procedure to enable a user to scroll a bitmap. The example code in Section 0.2.3.5, “Example: Scrolling a Bitmap” includes a menu item that copies the screen contents to a bitmap and displays the bitmap in the window's client area.
The code also processes the WM_VSCROLL and WM_HSCROLL messages generated by the scroll bars, so that the user may scroll the bitmap vertically and horizontally. Unlike the example that scrolled text, the bitmap example uses the BitBlt function to draw the invalid portion of the client area.
You should initialize the variables required for scrolling while processing the WM_CREATE message. You should also create a compatible device context with the CreateCompatibleDC function, create a bitmap with the CreateBitmap function, and select the bitmap into the device context with the SelectObject function.
This is also a convenient time to retrieve device-specific information about the display. If you create a memory device context for the screen, as in the example code, you can use the GetDeviceCaps function to get this information. The information includes the number of adjacent color bits per pixel, the number of color planes, and the height and width of the device context.
While processing the WM_SIZE message, you must adjust the scrolling range and scrolling position to reflect the dimensions of the client area and the bitmap to be displayed.
The SetScrollRange function sets the minimum and maximum position values for a scroll bar. The SetScrollPos function adjusts the scroll box to reflect the current scrolling position.
To process the WM_VSCROLL and WM_HSCROLL messages, you must examine the scroll bar notification code and set the scrolling position to a new value that reflects the scrolling action of the user. If the scrolling position is within the scrolling range, then you must scroll the window to the new position by using the ScrollWindow function and adjust the position of the scroll box by using the SetScrollPos function.
After scrolling a window, part of the client area will be invalid; therefore, the application must draw the appropriate part of the bitmap in the invalid region. The UpdateWindow function generates a WM_PAINT message to ensure that the invalid region is updated.
The following illustration shows an application's client area prior to calling the ScrollWindow function:
The next illustration shows the same application's client area after Window Manager has processed the ScrollWindow function:
The invalid region at the bottom of the client area must be repainted by the application when it processes the WM_PAINT message.
If the client area has been scrolled or resized, the example application uses the BitBlt function to copy the appropriate portion of the bitmap to the invalid portion of the client area.
The following illustration shows an application's client area after the application has processed the WM_PAINT message and called the BitBlt function to repaint the invalid region:
The following example enables the user to capture the screen contents into a bitmap and scroll the bitmap in the application window's client area:
HDC hdc;
PAINTSTRUCT ps;
/* These variables are required by BitBlt. */
static HDC hdcWin; /* window device context (DC) */
static HDC hdcScreen; /* DC for entire screen */
static HDC hdcScreenCompat; /* memory DC for screen */
static HBITMAP hbmpCompat; /* bitmap handle for old DC */
static BITMAP bmp; /* bitmap data structure */
static BOOL fBlt; /* TRUE if BitBlt occurred */
static BOOL fScroll; /* TRUE if scrolling occurred */
static BOOL fSize; /* TRUE if fBlt & WM_SIZE */
/* These variables are required for vertical scrolling. */
static int yMinScroll; /* minimum vertical scroll value */
static int yCurrentScroll; /* current vertical scroll value */
static int yMaxScroll; /* maximum vertical scroll value */
/* These variables are required for horizontal scrolling. */
static int xMinScroll; /* minimum horizontal scroll value */
static int xCurrentScroll; /* current horizontal scroll value */
static int xMaxScroll; /* maximum horizontal scroll value */
switch (uMsg) {
case WM_CREATE:
/*
* Create a normal DC and a memory DC for the entire
* screen. The normal DC provides a "snapshot" of the
* screen contents. The memory DC keeps a copy of this
* snapshot in the associated bitmap. (Note that a memory
* DC is sometimes referred to as a compatible DC.)
*/
hdcScreen = CreateDC("DISPLAY", (LPCSTR) NULL,
(LPCSTR) NULL, (CONST DEVMODE *) NULL);
hdcScreenCompat = CreateCompatibleDC(hdcScreen);
/*
* Retrieve the metrics for the bitmap associated with the
* normal device context.
*/
bmp.bmBitsPixel =
(BYTE) GetDeviceCaps(hdcScreen, BITSPIXEL);
bmp.bmPlanes = (BYTE) GetDeviceCaps(hdcScreen, PLANES);
bmp.bmWidth = GetDeviceCaps(hdcScreen, HORZRES);
bmp.bmHeight = GetDeviceCaps(hdcScreen, VERTRES);
/* The width must be byte aligned. */
bmp.bmWidthBytes = ((bmp.bmWidth + 15) &~15)/8;
/* Create a bitmap for the compatible DC. */
hbmpCompat = CreateBitmap(bmp.bmWidth, bmp.bmHeight,
bmp.bmPlanes, bmp.bmBitsPixel, (CONST VOID *) NULL);
/* Select the bitmap into the compatible DC. */
SelectObject(hdcScreenCompat, hbmpCompat);
/* Initialize flags. */
fBlt = FALSE;
fScroll = FALSE;
fSize = FALSE;
/* Initialize vertical scrolling variables. */
yMinScroll = 0;
yCurrentScroll = 0;
yMaxScroll = 0;
/* Initialize horizontal scrolling variables. */
xMinScroll = 0;
xCurrentScroll = 0;
xMaxScroll = 0;
break;
case WM_SIZE: {
int yNewSize;
int xNewSize;
yNewSize = HIWORD(lParam);
xNewSize = LOWORD(lParam);
if (fBlt)
fSize = TRUE;
/*
* The vertical scroll range defined by
* (bitmap_height) - (client_height).
*/
yMaxScroll = max(bmp.bmHeight - yNewSize, 0);
SetScrollRange(hwnd, SB_VERT,
yMinScroll, yMaxScroll, FALSE);
/*
* The current vertical scroll value is never out of the
* vertical scroll range.
*/
yCurrentScroll = min(yCurrentScroll, yMaxScroll);
SetScrollPos(hwnd, SB_VERT, yCurrentScroll, TRUE);
/*
* The horizontal scroll range defined by
* (bitmap_width) - (client_width).
*/
xMaxScroll = max(bmp.bmWidth-xNewSize, 0);
SetScrollRange(hwnd, SB_HORZ, xMinScroll, xMaxScroll,
FALSE);
/*
* The current horizontal scroll value is never out of the
* horizontal scroll range.
*/
xCurrentScroll = min(xCurrentScroll, xMaxScroll);
SetScrollPos(hwnd, SB_HORZ, xCurrentScroll, TRUE);
}
break;
case WM_PAINT: {
PRECT prect;
hdc = BeginPaint(hwnd, &ps);
/*
* If the window has been resized and the user has
* captured the screen, use the following call to
* BitBlt to fill the window's client area.
*/
if (fSize) {
BitBlt(ps.hdc,
0, 0,
bmp.bmWidth, bmp.bmHeight,
hdcScreenCompat,
xCurrentScroll, yCurrentScroll,
SRCCOPY);
fSize = FALSE;
}
/*
* If scrolling has occurred, use the following call to
* BitBlt to fill the invalid rectangle.
*
* The coordinates of this rectangle are specified in the
* RECT structure to which prect points.
*
* Note that it is necessary to increment the seventh
* argument (prect->left) by xCurrentScroll and the
* eighth argument (prect->top) by yCurrentScroll in
* order to map the correct pixels from the source bitmap.
*/
if (fScroll) {
prect = &ps.rcPaint;
BitBlt(ps.hdc,
prect->left, prect->top,
(prect->right - prect->left),
(prect->bottom - prect->top),
hdcScreenCompat,
prect->left + xCurrentScroll,
prect->top + yCurrentScroll,
SRCCOPY);
fScroll = FALSE;
}
EndPaint(hwnd, &ps);
}
break;
case WM_VSCROLL: {
int yDelta; /* yDelta = new_pos - current_pos */
int yNewPos; /* new position */
int xDelta = 0;
switch (LOWORD(wParam)) {
/* User clicked in the shaft above the scroll box. */
case SB_PAGEUP:
yNewPos = yCurrentScroll - 50;
break;
/* User clicked in the shaft below the scroll box. */
case SB_PAGEDOWN:
yNewPos = yCurrentScroll + 50;
break;
/* User clicked on the top arrow. */
case SB_LINEUP:
yNewPos = yCurrentScroll - 5;
break;
/* User clicked on the bottom arrow. */
case SB_LINEDOWN:
yNewPos = yCurrentScroll + 5;
break;
/* User dragged the scroll box. */
case SB_THUMBPOSITION:
yNewPos = HIWORD(wParam);
break;
default:
yNewPos = yCurrentScroll;
}
/* New position must be between 0 and the screen height. */
yNewPos = max(0, yNewPos);
yNewPos = min(yMaxScroll, yNewPos);
/* If the current position didn't change, don't scroll.*/
if (yNewPos == yCurrentScroll)
break;
/* Set the scroll flag to TRUE. */
fScroll = TRUE;
/* Determine the amount scrolled (in pixels). */
yDelta = yNewPos - yCurrentScroll;
/* Reset the current scroll position. */
yCurrentScroll = yNewPos;
/*
* Scroll the window. (The system will repaint most of the
* client area when ScrollWindow is called; however, it is
* necessary to call UpdateWindow in order to repaint the
* rectangle of pixels which were invalidated.)
*/
ScrollWindow(hwnd, -xDelta, -yDelta, (CONST RECT *) NULL,
(CONST RECT *) NULL);
UpdateWindow(hwnd);
/* Reset the scroll bar. */
SetScrollPos(hwnd, SB_VERT, yCurrentScroll, TRUE);
}
break;
case WM_HSCROLL: {
int xDelta; /* xDelta = new_pos - current_pos */
int xNewPos; /* new position */
int yDelta = 0;
switch (LOWORD(wParam)) {
/* User clicked the shaft left of the scroll box. */
case SB_PAGEUP:
xNewPos = xCurrentScroll - 50;
break;
/* User clicked the shaft right of the scroll box. */
case SB_PAGEDOWN:
xNewPos = xCurrentScroll + 50;
break;
/* User clicked on the left arrow. */
case SB_LINEUP:
xNewPos = xCurrentScroll - 5;
break;
/* User clicked on the right arrow. */
case SB_LINEDOWN:
xNewPos = xCurrentScroll + 5;
break;
/* User dragged the scroll box. */
case SB_THUMBPOSITION:
xNewPos = HIWORD(wParam);
break;
default:
xNewPos = xCurrentScroll;
}
/* New position must be between 0 and the screen width. */
xNewPos = max(0, xNewPos);
xNewPos = min(xMaxScroll, xNewPos);
/* If the current position didn't change, don't scroll.*/
if (xNewPos == xCurrentScroll)
break;
/* Set the scroll flag to TRUE. */
fScroll = TRUE;
/* Determine the amount scrolled (in pixels). */
xDelta = xNewPos - xCurrentScroll;
/* Reset the current scroll position. */
xCurrentScroll = xNewPos;
/*
* Scroll the window. (The system will repaint most of the
* client area when ScrollWindow is called; however, it is
* necessary to call UpdateWindow in order to repaint the
* rectangle of pixels which were invalidated.)
*/
ScrollWindow(hwnd, -xDelta, -yDelta, (CONST RECT *) NULL,
(CONST RECT *) NULL);
UpdateWindow(hwnd);
/* Reset the scroll bar. */
SetScrollPos(hwnd, SB_HORZ, xCurrentScroll, TRUE);
}
break;
case WM_COMMAND: /* uMsg: command from app. menu */
switch(wParam) {
case IDM_STC:
/*
* Copy the contents of the current screen
* into the compatible device context.
*/
BitBlt(hdcScreenCompat, 0, 0, bmp.bmWidth,
bmp.bmHeight, hdcScreen, 0, 0, SRCCOPY);
/*
* Copy the memory DC to the window's client area.
*/
hdcWin = GetDC(hwnd);
BitBlt(hdcWin, 0, 0, bmp.bmWidth, bmp.bmHeight,
hdcScreenCompat, 0, 0, SRCCOPY);
ReleaseDC(hwnd, hdcWin);
fBlt = TRUE;
break;
default:
return (DefWindowProc(hwnd, uMsg,
wParam, lParam));
}
break;
.
.
.