alpha49 2023-03-25 18:02 采纳率: 62.5%
浏览 84
已结题

请问纯C语言如何不使用easyx实现窗口绘图?

最近我在学习Windows窗口编程。之前使用easyx图形库编写了一个贪吃蛇小程序,但是easyx图形库只能用在vs上,且不支持在纯C语言中使用,而且之前尝试在32位系统上编译失败报错了,所以想要尝试弄懂easyx图形库的原理,自己使用纯C语言编写一个简易的图形库。

img

如上图所示,如果在一个黑色背景的窗口中绘制一个白色的矩形,使用easyx可以很简单的做到:
#include "graphics.h"

int main() {
    initgraph(800, 600);
    setlinecolor(WHITE);
    setfillcolor(WHITE);
    fillrectangle(50, 50, 100, 100);
    system("pause");
}
但是如果不使用easyx图形库来还原这个效果,我想到的方法是创建一个子窗口,代码如下:
#include <windows.h>

LRESULT CALLBACK ParentWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
    WNDCLASS wc;
    HWND hwndParent, hwndChild;
    MSG msg;

    // 注册父窗口类
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = ParentWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = TEXT("ParentWndClass");
    RegisterClass(&wc);

    // 注册子窗口类
    wc.lpfnWndProc = ChildWndProc;
    wc.lpszClassName = TEXT("ChildWndClass");
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    RegisterClass(&wc);

    // 创建父窗口
    hwndParent = CreateWindow(TEXT("ParentWndClass"),
                              TEXT("Parent Window"),
                              WS_OVERLAPPEDWINDOW,
                              CW_USEDEFAULT,
                              CW_USEDEFAULT,
                              300,
                              300,
                              NULL,
                              NULL,
                              hInstance,
                              NULL);

    ShowWindow(hwndParent, iCmdShow);
    UpdateWindow(hwndParent);

    // 消息循环
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

LRESULT CALLBACK ParentWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
        case WM_CREATE:
            // 创建子窗口
            CreateWindow(TEXT("ChildWndClass"),
                         TEXT("Child Window"),
                         WS_CHILD | WS_VISIBLE,
                         10,
                         10,
                         50,
                         50,
                         hwnd,
                         NULL,
                         ((LPCREATESTRUCT)lParam)->hInstance,
                         NULL);
            break;

        case WM_SIZE:
            // 调整子窗口的大小和位置
            MoveWindow(GetDlgItem(hwnd, 1),
                       10,
                       10,
                       LOWORD(lParam) - 100,
                       HIWORD(lParam) - 100,
                       TRUE);
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }

    return 0;
}

LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
        case WM_CREATE:
            break;

        case WM_PAINT:
        {
            PAINTSTRUCT ps;
        }
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }

    return 0;
}

运行结果:

img

但是这样虽然可以还原出来一模一样的效果,但如果每创建一个矩形就要创建一个子窗口,在编游戏程序时无疑会占用很大的内存,而且通过Spy++查找窗口,easyx绘制的窗口也不是创建子窗口来绘图的。请问如何才能做到不依赖easyx实现简易的窗口绘图?求解答,感谢。
  • 写回答

7条回答 默认 最新

  • OKX安生 2023-03-25 18:28
    关注
    该回答引用于gpt与OKX安生共同编写:
    
    • 该回答引用于gpt与OKX安生共同编写:

    实现窗口绘图可以使用 Windows API 中的 GDI (Graphics Device Interface)。GDI 是一组用于处理图形相关操作的函数和数据结构,包括画图、刷漆、字体等等。

    下面是一个使用 GDI 绘制矩形的例子:

    #include <windows.h>
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
        WNDCLASSEX wc;
        HWND hwnd;
        MSG msg;
    
        ZeroMemory(&wc, sizeof(WNDCLASSEX));
    
        wc.cbSize = sizeof(WNDCLASSEX);
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = WndProc;
        wc.hInstance = hInstance;
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        wc.lpszClassName = "WindowClass";
    
        RegisterClassEx(&wc);
    
        hwnd = CreateWindowEx(0,
                              "WindowClass",
                              "Hello, World!",
                              WS_OVERLAPPEDWINDOW,
                              300,
                              300,
                              500,
                              400,
                              NULL,
                              NULL,
                              hInstance,
                              NULL);
    
        ShowWindow(hwnd, nCmdShow);
        UpdateWindow(hwnd);
    
        while (GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        return msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
        HDC hdc;
        PAINTSTRUCT ps;
        RECT rect;
    
        switch (msg) {
            case WM_PAINT:
                hdc = BeginPaint(hwnd, &ps);
    
                GetClientRect(hwnd, &rect);
                SetTextColor(hdc, RGB(0, 0, 255));
                DrawText(hdc, "Hello, World!", -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
    
                SelectObject(hdc, GetStockObject(NULL_BRUSH));
                SetROP2(hdc, R2_COPYPEN);
                SetBkMode(hdc, TRANSPARENT);
                Rectangle(hdc, 100, 100, 300, 200);
    
                EndPaint(hwnd, &ps);
                break;
            case WM_DESTROY:
                PostQuitMessage(0);
                break;
            default:
                return DefWindowProc(hwnd, msg, wParam, lParam);
        }
    
        return 0;
    }
    

    在这个示例中,我首先创建了一个窗口类 WNDCLASSEX,并使用 RegisterClassEx 函数进行注册。接着,在窗口函数 WndProc 中处理 WM_PAINT 消息,使用 BeginPaint 来获取设备上下文句柄 HDC,然后通过 GDI 绘制矩形、文字等图形,最后使用 EndPaint 结束绘图。

    需要注意的是,GDI 编程需要了解一些 Windows API 的基本知识,例如如何创建和处理消息循环,如何获取设备上下文句柄,如何绘制各种图形等等。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
  • 「已注销」 2023-03-25 18:50
    关注

    参考gpt,实现窗口绘图的方法是使用OpenGL,它是一个跨平台的图形库,可以在不同的操作系统上使用。OpenGL可以通过操作系统提供的原生窗口进行绘图,而不需要创建子窗口,这样可以减少内存占用和提高绘图效率。

    OpenGL的绘图是基于3D空间的,但是可以通过设置投影矩阵来实现2D绘图。OpenGL的绘图方式是通过渲染管线来实现的,可以分为几个阶段:顶点着色器、图元装配、光栅化、片元着色器等。这里不再详细介绍OpenGL的绘图过程,有兴趣的读者可以自行查找相关资料学习。

    下面是一个使用OpenGL实现绘制矩形的例子:

    
    #include <windows.h>
    #include <GL/gl.h>
    #include <GL/glu.h>
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                       LPSTR lpCmdLine, int nCmdShow)
    {
        HWND hwnd;
        MSG msg;
        WNDCLASS wc = {0};
        wc.lpfnWndProc   = WndProc;
        wc.hInstance     = hInstance;
        wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
        wc.lpszClassName = TEXT("MyWindowClass");
        if(!RegisterClass(&wc)) {
            return 0;
        }
        hwnd = CreateWindow(TEXT("MyWindowClass"), TEXT("My Window"),
                            WS_OVERLAPPEDWINDOW,
                            CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
                            NULL, NULL, hInstance, NULL);
        ShowWindow(hwnd, nCmdShow);
        UpdateWindow(hwnd);
        while(GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        return msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        HDC hdc;
        PAINTSTRUCT ps;
        switch(msg)
        {
        case WM_PAINT:
            hdc = BeginPaint(hwnd, &ps);
            // 设置视口大小
            glViewport(0, 0, ps.rcPaint.right - ps.rcPaint.left,
                       ps.rcPaint.bottom - ps.rcPaint.top);
            // 设置投影矩阵为正交投影
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            glOrtho(0, ps.rcPaint.right - ps.rcPaint.left,
                    ps.rcPaint.bottom - ps.rcPaint.top, 0, 0, 1);
            // 设置模型视图矩阵
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            // 设置颜色
            glColor3f(1.0, 1.0, 1.0);
            // 绘制矩形
            glBegin(GL_QUADS);
            glVertex2f(50, 50);
            glVertex2f(100, 50);
            glVertex2f(100, 100);
            glVertex2f(50, 100);
            glEnd();
            // 刷新缓冲区
            glFlush();
    SwapBuffers(hdc);
    
    // 用黑色清空屏幕
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    
    // 绘制一个白色的矩形
    glColor3f(1.0f, 1.0f, 1.0f);
    glBegin(GL_QUADS);
    glVertex2f(-0.5f, -0.5f);
    glVertex2f(-0.5f, 0.5f);
    glVertex2f(0.5f, 0.5f);
    glVertex2f(0.5f, -0.5f);
    glEnd();
    
    // 绘制完成后交换缓冲区
    glFlush();
    SwapBuffers(hdc);
    
    
    
    

    在这个代码中,我们使用 OpenGL 的方式绘制了一个白色矩形。首先,我们用 glClearColor 函数将屏幕清空为黑色,然后使用 glClear 函数清空颜色缓冲区。接下来,我们使用 glColor3f 函数指定颜色为白色,然后使用 glBegin(GL_QUADS) 函数开始绘制一个四边形。我们通过 glVertex2f 函数指定四个点的坐标,分别是左下角、左上角、右上角和右下角,形成了一个矩形。最后,我们使用 glEnd 函数结束绘制。绘制完成后,我们使用 glFlush 和 SwapBuffers 函数交换缓冲区,将矩形显示在屏幕上。

    需要注意的是,在每次绘制之前,我们都需要清空颜色缓冲区,否则之前绘制的图形会留在屏幕上,影响后续的绘制。另外,由于 OpenGL 是使用矩阵进行绘制的,我们需要使用矩阵变换来控制绘制的位置和大小,这个可以在后续的学习中逐步了解。

    综上所述,使用 OpenGL 来进行窗口绘制可以避免依赖第三方库,而且可以更灵活地控制绘制效果。但是需要注意的是,OpenGL 是一个底层的绘图 API,需要自己编写大量的代码来实现绘图功能,因此相对于使用现成的图形库,需要投入更多的时间和精力。

    评论
  • 我爱OJ 2023-03-25 19:14
    关注

    该回答引用ChatGPT
    这是个例子,你可以看一下:

    #include <windows.h>
    
    LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
        WNDCLASSEX windowClass = {0};
        HWND hwnd = NULL;
        MSG messages = {0};
        HDC hdc = NULL;
        PAINTSTRUCT ps;
        RECT windowRect = {0};
    
        windowClass.lpfnWndProc = WindowProc;
        windowClass.hInstance = hInstance;
        windowClass.lpszClassName = "WindowClass";
    
        if (!RegisterClassEx(&windowClass))
        {
            return 0;
        }
    
        windowRect.right = 640;
        windowRect.bottom = 480;
    
        AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE);
    
        hwnd = CreateWindowEx(0,
                              "WindowClass",
                              "Hello, World!",
                              WS_OVERLAPPEDWINDOW,
                              300,
                              300,
                              windowRect.right - windowRect.left,
                              windowRect.bottom - windowRect.top,
                              NULL,
                              NULL,
                              hInstance,
                              NULL);
    
        ShowWindow(hwnd, nCmdShow);
    
        while (GetMessage(&messages, NULL, 0, 0))
        {
            TranslateMessage(&messages);
            DispatchMessage(&messages);
        }
    
        return messages.wParam;
    }
    
    LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
        case WM_PAINT:
            {
                HDC hdc = BeginPaint(hwnd, &ps);
                Rectangle(hdc, 100, 100, 200, 200);
                EndPaint(hwnd, &ps);
            }
            break;
        case WM_DESTROY:
            {
                PostQuitMessage(0);
                return 0;
            }
        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
    
        return 0;
    }
    

    在该程序中,通过调用Windows API函数建立了一个窗口。在 WindowProc 函数中处理了窗口消息,例如当接收到 WM_PAINT 消息时,绘制了一个矩形。

    注意一下,在 Windows 10 中,微软已经针对 C++/WinRT 提供了更加易用的 API,以简化 Windows 应用程序的编写。

    评论
  • 四海一叶秋 2023-03-25 19:22
    关注
    评论
  • curating 2023-03-25 19:54
    关注

    DevC++,添加 graphics.h和libbgi. a文件。
    在链接器标签中粘贴以下内容:-lbgi -lgdi32 -lcomdlg32 -luuid -loleaut32 -lole32。
    参考 :Simple Graphic Program Using Dev-C++
    https://www.instms.com/cpp/graphics-using-dev-c++

    #include<graphics.h>
    
    ![img](https://img-mid.csdnimg.cn/release/static/image/mid/ask/608090547976129.jpg "#left")
    
    int main( ){
        initwindow( 400 , 400 , "Graphics using Dev-C++");
        setcolor(YELLOW);
        for(int i=5; i<=150; i+=5) {
        circle(200, 200, i);
        }
            
        getch();
        return 0;
    }
    
    

    img

    评论
  • ksgpjhqf 2023-03-25 20:43
    关注

    可以用winapi,需要包含windows.h

    评论
  • CSDN-Ada助手 CSDN-AI 官方账号 2023-03-28 03:35
    关注
    不知道你这个问题是否已经解决, 如果还没有解决的话:

    如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^
    评论
查看更多回答(6条)

报告相同问题?

问题事件

  • 系统已结题 4月2日
  • 已采纳回答 3月25日
  • 创建了问题 3月25日

悬赏问题

  • ¥15 MYSQL 订单的商品明细重复计算问题
  • ¥15 微信实时共享位置修改
  • ¥100 TG的session协议号转成直登号号后客户端登录几分钟后自动退出设备
  • ¥30 共模反馈回路的小信号增益
  • ¥15 arduino ssd1306函数与tone函数放歌代码不兼容问题
  • ¥70 0.96版本hbase的row_key里含有双引号,无法deleteall
  • ¥40 Ida Pro增加插件出现问题
  • ¥15 诊断性META分析合并效能的检验
  • ¥15 请问abb根据色块判断奇偶数并根据批次号放入仓储
  • ¥66 开发PC客户端一定也要开发上位机吗?