如何在winapi中实现双重缓冲? [英] How do I implement double buffering in the winapi?

查看:98
本文介绍了如何在winapi中实现双重缓冲?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法停止闪烁.我得到了添加dubbel缓冲的建议.我该怎么办?

I can't stop the flickering. I got the advice to add dubbel-buffering. How do I do that?

#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;
}

推荐答案

正如成员退休忍者所说,您要使设备上下文与原始上下文(在您的情况下为hdc)兼容,并创建与原始设备上下文兼容的位图(位图大小等于在其上绘制内容的矩形的大小).

As member Retired Ninja said, you make compatible device context with the original one ( hdc in your case ), and create a bitmap compatible with your original device context ( bitmap size is equal to the size of your rectangle where you paint your stuff ).

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

Then select this newly created bitmap into compatible device context you just created and draw everything on it.

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

Then you just BitBlt(...) the compatible device context into original one.

不要忘记执行适当的清理操作,以避免GDI泄漏.

Do not forget to perform proper cleanup in order to avoid GDI leaks.

您的代码应如下所示:

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;
    }

这篇关于如何在winapi中实现双重缓冲?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆