代码之家  ›  专栏  ›  技术社区  ›  Michael

每帧中的WinApi绘图

  •  1
  • Michael  · 技术社区  · 7 年前

    我想在每一帧上画一个正弦波。在每一帧中,绘制后,角度会增加,我想有一个平滑的波浪移动效果,但我不知道如何在每一帧中实现绘制。

    #include <windows.h>
    #include <cmath>
    #include <iostream>
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
    HWND hwnd;
    float t = 0.0f;
    
    HANDLE tickThreadHandle;
    
    DWORD WINAPI tickThreadProc(HANDLE handle)
    {
        Sleep(50);
        ShowWindow(hwnd, SW_SHOW);
    
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
    
        RECT r;
        GetWindowRect(hwnd, &r);
    
        HPEN hPen = CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
        HPEN hOldPen = static_cast<HPEN>(SelectObject(hdc, hPen));
    
        int delay = 1000 / 50;
    
        while (true)
        {
            for (int x = 0; x<r.right; x += 5, t += 0.3f)
            {
                LineTo(hdc, x, sin(t) * 50 + 200);
            }
    
            t += 0.1f;
            Sleep(delay);
        }
    
        SelectObject(hdc, hOldPen);
        DeleteObject(hPen);
        EndPaint(hwnd, &ps);
    }
    
    int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
    {
        LPCTSTR lpszClassName = L"WNDCLASS";
    
        WNDCLASSEX wcex;
        ZeroMemory(&wcex, sizeof(wcex));
    
        wcex.cbSize = sizeof(wcex);
        wcex.hInstance = hInstance;
        wcex.lpszClassName = lpszClassName;
        wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
        wcex.style = CS_DBLCLKS;
        wcex.lpfnWndProc = WndProc;
        wcex.hbrBackground = CreateSolidBrush(RGB(0, 0, 150));
    
        if (!RegisterClassEx(&wcex))
        {
            return -1;
        }
    
        hwnd = CreateWindowEx(
            NULL,
            lpszClassName,
            L"WINDOW",
            WS_OVERLAPPEDWINDOW,
            0,
            0,
            400,
            400,
            HWND_DESKTOP,
            NULL,
            hInstance,
            NULL
        );
    
        if (hwnd == NULL)
        {
            return -2;
        }
    
        //ShowWindow(hwnd, nCmdShow);
        UpdateWindow(hwnd);
        //InvalidateRect(hwnd, NULL, TRUE);
    
        MSG msg;
        //SendMessage(hwnd, WM_PAINT, NULL, NULL);
    
        while (GetMessage(&msg, hwnd, 0, 0) > 0)
        {
            /*RedrawWindow(hwnd, &windowRect, NULL, RDW_INTERNALPAINT);*/
            DispatchMessage(&msg);
        }
    
        return (int)msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
            case WM_CREATE:
            {
                tickThreadHandle = CreateThread(NULL, NULL, &tickThreadProc, NULL, NULL, NULL);
            }
            case WM_DESTROY:
            {
                PostQuitMessage(NULL);
            }
            //case WM_PAINT:
            //{
    
            //}
            default:
            {
                return DefWindowProc(hwnd, uMsg, wParam, lParam);
            }
        }
    
        return 0;
    }
    

    我尝试了很多策略,在我的最后一次尝试中,我试图做另一个线程,并从那里画,但我的窗口甚至没有显示。我正试图向我的窗口发送消息 WM_PAINT 也可以使用 RedrawWindow 功能,但这没有给我什么。我知道我可能用错了,请纠正我,并给我一个提示,我可以做什么。

    1 回复  |  直到 4 年前
        1
  •  3
  •   Barmak Shemirani    7 年前

    SetTimer 可以使用(与正常消息循环一起)以短至16毫秒的间隔绘制窗口。Windows将发送 WM_TIMER 您可以使窗口无效的每个间隔的消息。

    LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
        case WM_CREATE:
            SetTimer(hwnd, 1, 20, NULL); 
            break;
    
        case WM_TIMER:
            InvalidateRect(hwnd, NULL, FALSE);
            break;
    
        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            render(hwnd, hdc);
            EndPaint(hwnd, &ps);
            break;
        }
        ...
    }
    

    可以修改渲染函数以使用 HDC 从绘制功能。

    void render(HWND hwnd, HDC hdc)
    {
        ...
        HBRUSH hbrush = CreateSolidBrush(RGB(0, 0, 150));
        HBRUSH oldbrush = (HBRUSH)SelectObject(hdc, hbrush);
        FillRect(hdc, &cr, hbrush); 
        ...
        SelectObject(hdc, oldbrush);
        DeleteObject(hbrush);
        //Sleep <== remove the Sleep function
    }
    

    或者,您可以使用所谓的“游戏循环”和高分辨率计时器,如 std::chrono 以较短的间隔使窗口无效。 WM_PAINT 可以像以前一样处理消息。

    Direct3D或OpenGL游戏可以使用类似的消息循环并调用 render() InvalidateRect 油漆窗户。

    #include <chrono>
    ...
    
    MSG msg = { 0 };
    while(TRUE)
    {
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT) //<=== **** EDITED **** 
                break;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
            typedef std::chrono::high_resolution_clock hiresclock;
            static auto timer = hiresclock::now();
            auto milisec = (hiresclock::now() - timer).count() / 1000000;
            if(milisec > 20)
            {
                timer = hiresclock::now();
                //... draw
            }
        }
    }