Example of Scrolling a Bitmap

The following example enables the user to capture the screen content into a bitmap and scroll the bitmap in the client area.

HDC hdc;

PAINTSTRUCT ps;

SCROLLINFO si;

/* These variables are required by BitBlt. */

static HDC hdcWin; /* window 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 horizontal scrolling. */

static int xMinScroll; /* minimum horizontal scroll value */

static int xCurrentScroll; /* current horizontal scroll value */

static int xMaxScroll; /* maximum horizontal scroll value */

/* 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 */

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.

*/

hdcScreen = CreateDC("DISPLAY", (LPCSTR) NULL,

(LPCSTR) NULL, (CONST DEVMODE *) NULL);

hdcScreenCompat = CreateCompatibleDC(hdcScreen);

/*

* Retrieve the metrics for the bitmap associated with the

* regular 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 for the compatible DC. */

SelectObject(hdcScreenCompat, hbmpCompat);

/* Initialize the flags. */

fBlt = FALSE;

fScroll = FALSE;

fSize = FALSE;

/* Initialize the horizontal scrolling variables. */

xMinScroll = 0;

xCurrentScroll = 0;

xMaxScroll = 0;

/* Initialize the vertical scrolling variables. */

yMinScroll = 0;

yCurrentScroll = 0;

yMaxScroll = 0;

break;

case WM_SIZE: {

int xNewSize;

int yNewSize;

xNewSize = LOWORD(lParam);

yNewSize = HIWORD(lParam);

if (fBlt)

fSize = TRUE;

/*

* The horizontal scrolling range is defined by

* (bitmap_width) - (client_width). The current horizontal

* scroll value remains within the horizontal scrolling range.

*/

xMaxScroll = max(bmp.bmWidth-xNewSize, 0);

xCurrentScroll = min(xCurrentScroll, xMaxScroll);

si.cbSize = sizeof(si);

si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;

si.nMin = xMinScroll;

si.nMax = xMaxScroll;

si.nPage = xNewSize;

si.nPos = xCurrentScroll;

SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);

/*

* The vertical scrolling range is defined by

* (bitmap_height) - (client_height). The current vertical

* scroll value remains within the vertical scrolling range.

*/

yMaxScroll = max(bmp.bmHeight - yNewSize, 0);

yCurrentScroll = min(yCurrentScroll, yMaxScroll);

si.cbSize = sizeof(si);

si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;

si.nMin = yMinScroll;

si.nMax = yMaxScroll;

si.nPage = yNewSize;

si.nPos = yCurrentScroll;

SetScrollInfo(hwnd, SB_VERT, &si, 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 paint 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 paint 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_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 the left arrow. */

case SB_LINEUP:

xNewPos = xCurrentScroll - 5;

break;

/* User clicked 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 does not change, do not 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 repaints most of the

* client area when ScrollWindowEx is called; however, it is

* necessary to call UpdateWindow in order to repaint the

* rectangle of pixels that were invalidated.)

*/

ScrollWindowEx(hwnd, -xDelta, -yDelta, (CONST RECT *) NULL,

(CONST RECT *) NULL, (HRGN) NULL, (LPRECT) NULL,

SW_INVALIDATE);

UpdateWindow(hwnd);

/* Reset the scroll bar. */

si.cbSize = sizeof(si);

si.fMask = SIF_POS;

si.nPos = xCurrentScroll;

SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);

}

break;

case WM_VSCROLL: {

int xDelta = 0;

int yDelta; /* yDelta = new_pos - current_pos */

int yNewPos; /* new position */

switch (LOWORD(wParam)) {

/* User clicked the shaft above the scroll box. */

case SB_PAGEUP:

yNewPos = yCurrentScroll - 50;

break;

/* User clicked the shaft below the scroll box. */

case SB_PAGEDOWN:

yNewPos = yCurrentScroll + 50;

break;

/* User clicked the top arrow. */

case SB_LINEUP:

yNewPos = yCurrentScroll - 5;

break;

/* User clicked 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 does not change, do not 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 repaints most of the

* client area when ScrollWindowEx is called; however, it is

* necessary to call UpdateWindow in order to repaint the

* rectangle of pixels that were invalidated.)

*/

ScrollWindowEx(hwnd, -xDelta, -yDelta, (CONST RECT *) NULL,

(CONST RECT *) NULL, (HRGN) NULL, (LPRECT) NULL,

SW_INVALIDATE);

UpdateWindow(hwnd);

/* Reset the scroll bar. */

si.cbSize = sizeof(si);

si.fMask = SIF_POS;

si.nPos = yCurrentScroll;

SetScrollInfo(hwnd, SB_VERT, &si, 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 DC.

*/

BitBlt(hdcScreenCompat, 0, 0, bmp.bmWidth,

bmp.bmHeight, hdcScreen, 0, 0, SRCCOPY);

/*

* Copy the compatible DC to the 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;