c++ - WinApi 在每一帧中绘图

标签 c++ c animation winapi rendering

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

这是我的代码:

#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 函数,但这没有给我任何结果。我知道我可能会以错误的方式使用它,请纠正我并给我一个提示我可以做什么。

最佳答案

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

关于c++ - WinApi 在每一帧中绘图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47144021/

相关文章:

c++ - double - 小数位

c++ - 使用 std::wstring 或 wchar_t 进行 Poco 日志记录

javascript - Angular 动画的目的是什么?

c - 在另一个 .c 文件上声明函数使浮点乘法始终为 0

c - 避免我的应用程序将 Return 读取为字符

ios - viewDidLoad/viewDidAppear 的 UIView 动画?

jQuery 如果鼠标悬停 2 秒。开始动画或根本不动画

c++ - STL map 在删除第一对后不会添加一对

python - 使用 Python 从 C++ dll 返回数据

c - 如何在C中将图像排序成行