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;