如何在winapi中实现双缓冲?

时间:2014-08-23 09:14:45

标签: winapi double-buffering

我无法阻止闪烁。我得到了添加dubbel-buffering的建议。我该怎么做?

#include <iostream>
#include <windows.h>
#include <string>
#include <fstream>
#include <vector>

using namespace std;

namespace {
    const int ID_NEW = 1;
    const int ID_QUIT = 2;
    const int ID_ABOUT = 3;
    const int NORTH_BUTTON_ID = 4;
    const int SOUTH_BUTTON_ID = 5;
    const int WEST_BUTTON_ID = 6;
    const int EAST_BUTTON_ID = 7;
    const int ID_FINISHED_GAME = 8;
    int x = 0;
    int y = 0;
    int xStart = 0;
    int yStart = 0;
    int windowHeight = 400;
    int windowWidth = 500;
    char level1[20][21];
    int noOfMoves = 0;
}

void readLevel(string fileName, char level[20][21]) {
    char character{};
    ifstream file(fileName);
    int i = 0;
    int j = 0;
    if (file.is_open()) {
        while (file >> character) {
            level[j][i] = character;
            if (level[j][i] == 's') {
                y = yStart = j;
                x = xStart = i;
            }
            if (++i % 20 == 0) {
                i = 0;
                j++;
            }
        }
        file.close();
    }
}

void restart(){
    x = xStart;
    y = yStart;
    noOfMoves = 0;
}

LRESULT CALLBACK WinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    HDC hdc{ 0 };
    PAINTSTRUCT ps{ 0 };
    switch (msg) {
    case WM_CLOSE:
        PostQuitMessage(0);
        return 0;
    case WM_COMMAND:
        switch (LOWORD(wParam)){
        case ID_ABOUT:
            MessageBox(hwnd, L"About this program!", L"About", MB_OK);
            return 0;
        case ID_NEW:
            restart();
            return 0;
        case ID_QUIT:
            if (MessageBox(0, L"Do you really want to quit?", L"Are you sure?", MB_YESNO) == IDYES) {
                PostQuitMessage(0);
                return 0;
            }
        case NORTH_BUTTON_ID:
            if (level1[y - 1][x] != '1') {
                y -= 1;
                noOfMoves++;
            }
            break;
        case SOUTH_BUTTON_ID:
            if (level1[y + 1][x] != '1'){
                y += 1;
                noOfMoves++;
            }
            break;
        case WEST_BUTTON_ID:
            if (level1[y][x - 1] != '1'){
                x -= 1;
                noOfMoves++;
            }
            break;
        case EAST_BUTTON_ID:
            if (level1[y][x + 1] != '1') {
                x += 1;
                noOfMoves++;
            }
            break;
        }
        if (level1[y][x] == 'e') {
            wstring moves = L"Congratulations, you finished the game with " + to_wstring(noOfMoves);
            MessageBox(hwnd, moves.c_str(), L"Finished game", MB_OK);
        }
    case WM_PAINT: {
                       char wall[2] = { "W" };
                       char floor[2] = { 'W' };
                       char current[2] = { "X" };
                       char goal[2] = { "G" };
                       wstring position = L"Position = [" + to_wstring(x) + L", " + to_wstring(y) + L"]";
                       wstring moves = L"Move = " + to_wstring(noOfMoves);
                       hdc = BeginPaint(hwnd, &ps);
                       TextOut(hdc, 20, 200, position.c_str(), position.size());
                       TextOut(hdc, 20, 220, moves.c_str(), moves.size());
                       for (int i = 0; i < 20; i++) {
                           for (int j = 0; j < 20; j++) {
                               if (level1[j][i] == '1') {
                                   SetTextColor(hdc, RGB(0, 0, 0));
                                   SetBkColor(hdc, RGB(0, 0, 0));
                                   TextOut(hdc, 14 * i + 190, 14 * j + 20, LPCTSTR(wall), strlen(wall));
                               }
                               SetBkColor(hdc, RGB(255, 255, 255));
                               if (level1[j][i] == '0') {
                                   SetTextColor(hdc, RGB(255, 255, 255));
                                   TextOut(hdc, 14 * i + 190, 14 * j + 20, LPCTSTR(floor), strlen(floor));
                               }
                               SetTextColor(hdc, RGB(0, 0, 0));
                               if (i == x && j == y)
                                   TextOut(hdc, 14 * i + 190, 14 * j + 20, LPCTSTR(current), strlen(current));
                               if (level1[j][i] == 'e')
                                   TextOut(hdc, 14 * i + 190, 14 * j + 20, LPCTSTR(goal), strlen(goal));
                           }
                       }
                       EndPaint(hwnd, &ps);
                       break;

    }
    case WM_ERASEBKGND:
        return true;
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

HMENU CreateMainMenu() {
    HMENU main = CreateMenu();
    HMENU file = CreateMenu();
    AppendMenu(file, MF_STRING, ID_NEW, L"&New");
    AppendMenu(file, MF_SEPARATOR, 0, 0);
    AppendMenu(file, MF_STRING, ID_QUIT, L"&Quit");
    AppendMenu(main, MF_POPUP, (UINT_PTR)file, L"&File");
    HMENU help = CreateMenu();
    AppendMenu(help, MF_STRING, ID_ABOUT, L"&About");
    AppendMenu(main, MF_POPUP, (UINT_PTR)help, L"&Help");
    return main;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow) {
    readLevel("level1.txt", level1);
    WNDCLASS wc = { 0 };
    wc.hbrBackground = NULL;
    wc.lpfnWndProc = WinProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = L"MyWindowClass";
    RegisterClass(&wc);
    HWND hwnd = CreateWindow(L"MyWindowClass", L"The Maze",
        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
        windowWidth, windowHeight, 0, CreateMainMenu(), hInstance, 0);
    HWND buttonNorth = CreateWindow(L"BUTTON", L"NORTH", WS_CHILD | WS_VISIBLE,
        10, 20, 150, 40, hwnd, (HMENU)NORTH_BUTTON_ID, hInstance, 0);
    HWND buttonSouth = CreateWindow(L"BUTTON", L"SOUTH", WS_CHILD | WS_VISIBLE,
        10, 60, 150, 40, hwnd, (HMENU)SOUTH_BUTTON_ID, hInstance, 0);
    HWND buttonEast = CreateWindow(L"BUTTON", L"EAST", WS_CHILD | WS_VISIBLE,
        10, 140, 150, 40, hwnd, (HMENU)EAST_BUTTON_ID, hInstance, 0);
    HWND buttonWest = CreateWindow(L"BUTTON", L"WEST", WS_CHILD | WS_VISIBLE,
        10, 100, 150, 40, hwnd, (HMENU)WEST_BUTTON_ID, hInstance, 0);
    UpdateWindow(hwnd);
    ShowWindow(hwnd, nCmdShow);
    MSG msg = { 0 };
    BOOL isRunning = true;
    while (isRunning) {
        while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
            if (msg.message == WM_QUIT)
                isRunning = false;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        wc.hbrBackground = NULL;
        InvalidateRect(hwnd, NULL, FALSE);
        Sleep(10);
    }
    return 0;
}

1 个答案:

答案 0 :(得分:2)

当成员 Retired Ninja 表示,您使用原始设备(hdc创建兼容设备上下文),并创建与原始设备上下文兼容的位图(位图大小为等于你画出你的东西的矩形的大小)。

然后将这个新创建的位图选择到刚刚创建的兼容设备上下文中,并在其上绘制所有内容。

然后您只需将BitBlt(...)兼容设备上下文转换为原始设备上下文。

不要忘记进行适当的清理以避免GDI泄漏。

您的代码应如下所示:

case WM_PAINT: 
    {
        // skipped the initialization part to preserve space
        // just copy those, they are irrelevant for your problem

        hdc = BeginPaint(hwnd, &ps);

        // create memory DC and memory bitmap where we shall do our drawing

        HDC memDC = CreateCompatibleDC( hdc );

        // get window's client rectangle. We need this for bitmap creation.
        RECT rcClientRectangle;
        GetClientRect( hwnd, &rcClientRect );

        // now we can create bitmap where we shall do our drawing
        HBITMAP bmp = CreateCompatibleBitmap( hdc, 
            rcClientRect.right - rcClientRect.left, 
            rcClientRect.bottom - rcClientRect.top );

        // we need to save original bitmap, and select it back when we are done,
        // in order to avoid GDI leaks!
        HBITMAP oldBmp = (HBITMAP)SelectObject( memDC, bmp );

        // now you draw your stuff in memory dc; 
        // just substitute hdc with memDC in your drawing code, 
        // like I did below:

        TextOut( memDC, //...
        TextOut( memDC, //...
        for (int i = 0; i < 20; i++) 
        {
            for (int j = 0; j < 20; j++) 
            {
                if (level1[j][i] == '1') 
                {
                    SetTextColor( memDC, //...
                    SetBkColor( memDC, //...
                    TextOut( memDC, //...
                }
                SetBkColor( memDC, //...
                if (level1[j][i] == '0') 
                {
                    SetTextColor( memDC, //...
                    TextOut( memDC, //...
                }
                SetTextColor( memDC, //...
                if (i == x && j == y)
                    TextOut( memDC, //...
                if (level1[j][i] == 'e')
                    TextOut( memDC, //...
            }
        }

        // OK, everything is drawn into memory DC, 
        // now is the time to draw that final result into our target DC

        BitBlt( hdc, 0, 0, rcClientRect.right - rcClientRect.left, 
            rcClientRect.bottom - rcClientRect.top, memDC, 0, 0, SRCCOPY );

        // all done, now we need to cleanup
        SelectObject( memDC, oldBmp ); // select back original bitmap
        DeleteObject( bmp ); // delete bitmap since it is no longer required
        DeleteDC( memDC );   // delete memory DC since it is no longer required

        EndPaint(hwnd, &ps);
        break;
    }