请问纯C语言如何编写简易的easyx图形库

我最近想自己动手写一个easyx绘图库,我现在已经使用多线程写出了如下代码:
#include "windows.h"
#include "stdio.h"
#include 

HWND h;
HINSTANCE hinst;

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

void win(){
    WNDCLASSEX wc;
    HWND hwnd;

    hinst = GetModuleHandle(NULL);

    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hinst;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszClassName = "WindowClass";
    wc.hbrBackground = CreateSolidBrush(RGB(127, 127, 127));

    RegisterClassEx(&wc);

    hwnd = CreateWindowEx(0,
                          "WindowClass",
                          "Hello, World!",
                          WS_OVERLAPPEDWINDOW,
                          300,
                          300,
                          500,
                          400,
                          NULL,
                          NULL,
                          hinst,
                          NULL);

    ShowWindow(hwnd, SW_SHOWDEFAULT);
    UpdateWindow(hwnd);
    h = hwnd;


}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }

    return 0;
}

void gmsg(){
    MSG msg;
    win();
    while (GetMessage(&msg, h, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

HWND initgraph(){
    pthread_t th1;

    pthread_create(&th1,NULL,(void*)gmsg,NULL);

    pthread_detach(th1);

    return h;
}

int main(){
    HWND hwnd = initgraph();
    printf("hello");
    getchar();
}
其中initgraph();函数是用来创建多线程窗口的,但是往下就没有思路了,如easyx库中创建窗口后使用setlinecolor改变线的颜色、setfillcolor改变填充色、fillrectangle函数绘制填充矩形等。以画矩形为例,如何创建一个函数,使其调用之后就可以实现与easyx一样的绘图效果呢?求解答,谢谢。

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
要使用纯C语言实现类似EasyX的图形库是相当困难的任务。EasyX库充分利用Windows GDI的功能,调用了大量的Windows API,展现了一个简洁易用、功能强大且优秀的图形程序开发库。

如果您想使用C语言开发一个图形库,我们建议您使用OpenGL或者SDL库,它们都是跨平台的图形库,非常适用于游戏开发。

如果您坚持要使用Windows API来实现类似EasyX的库,我们将为您提供以下几个步骤:

1.创建窗口

和你现在写的代码类似,使用Windows API创建一个窗口,可以使用CreateWindow函数来创建并显示出一个窗口。

2.处理消息

和你现在写的代码一样,在WndProc函数中处理窗口消息。

3.创建画笔

使用Windows API中的CreatePen和CreateSolidBrush函数来创建画笔和画刷。

4.绘制图形

使用Windows API中的各种绘图函数,例如LineTo、MoveToEx、Rectangle、Ellipse等来绘制需要的图形。

5.更新屏幕

使用Windows API中的InvalidateRect和UpdateWindow函数来更新界面。

以下是简单的代码示例:

#include <windows.h>

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

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
    char szClassName[] = "MyClass";
    char szTitle[] = "EasyX-like";

    WNDCLASSEX wcex;
    ZeroMemory(&wcex, sizeof(wcex));
    wcex.cbSize = sizeof(wcex);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.hInstance = hInstance;
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.lpszClassName = szClassName;
    RegisterClassEx(&wcex);

    HWND hWnd = CreateWindow(szClassName, szTitle, WS_OVERLAPPEDWINDOW, 50, 50, 700, 500, NULL, NULL, hInstance, NULL);
    ShowWindow(hWnd, SW_SHOWDEFAULT);

    HDC hdc = GetDC(hWnd);
    HPEN pen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
    SelectObject(hdc, pen);

    RECT rc = { 50, 50, 150, 150}; // 左上角坐标为 (50, 50),右下角坐标为 (150, 150)
    Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);

    ReleaseDC(hWnd, hdc);
    MessageBox(NULL, "Hello", "World", MB_OK);

    return 0;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);

            HBRUSH brush = CreateSolidBrush(RGB(0, 0, 255));
            SelectObject(hdc, brush);

            RECT rc = {200, 100, 300, 200};
            FillRect(hdc, &rc, brush);

            EndPaint(hwnd, &ps);
            break;
        }
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

这份代码创建了一个350 * 400像素的窗口,并在窗口中绘制了一个红色矩形和一个蓝色填充矩形。

由于Windows GDI中的所有函数都是基于设备上下文(DC)的,所以需要在绘图函数中获取设备上下文。在这个示例中,我们使用了GetDC和ReleaseDC函数来获取和释放设备上下文。

这只是一个初步的示例,希望能过帮助您进一步了解图形编程和Windows API。如果您想要实现更加高级的图形库,我们建议您深入学习Windows GDI的相关知识。
如果我的回答解决了您的问题,请采纳!

easyx能支持多线程?悬

其实就是你自己维护一套绘图属性而已,窗口和hdc自己维护,
例如客户调用circle(200, 200, 100);
在你的库里直接依据你保存的绘图属性,在你创建的窗口调用相关的gdi函数绘制一个圆就可以,这个模式可能效率比较低,你也可以自己维护一套类似gdi的东西,在自己的画布画好,最后直接setbits到窗口

回答引用ChatGPT:
EasyX并不直接支持多线程,它是一个单线程的绘图库。在EasyX中,所有的绘图操作都是在主线程中完成的,如果在其他线程中调用绘图函数,会出现不可预料的结果。
但是,在EasyX中可以使用一些基本的多线程技术,比如使用Win32 API中的CreateThread()函数创建一个新线程,在新线程中进行一些计算操作,然后将结果通过一些通信方式传递给主线程,由主线程完成绘图操作。这样可以实现一些简单的多线程绘图效果,但是需要注意多线程并发操作时的同步问题,避免出现线程安全问题。
以下使用一些基本的多线程来处理这个问题,实现类似于easyx库中的绘图效果。
首先,需要创建一个结构体,用于存储图形的属性,包括线条颜色、填充颜色、线宽等信息:

typedef struct tagGRAPHICS
{
    COLORREF lineColor;     // 线条颜色
    COLORREF fillColor;    // 填充颜色
    int lineWidth;          // 线宽
    int x1, y1, x2, y2;     // 矩形的左上角和右下角坐标
} GRAPHICS;

然后,创建一个绘图线程,用于实时更新画面。绘图线程会在窗口创建后自动启动,并将绘图所需的数据通过队列传递给主线程。主线程负责将队列中的数据取出并绘制出来。

// 线程绘图函数
DWORD WINAPI GraphicsThread(LPVOID lpParam)
{
    // 初始化队列
    queue<GRAPHICS> graphicsQueue;
    CRITICAL_SECTION cs;
    InitializeCriticalSection(&cs);

    // 获取窗口句柄
    HWND hwnd = (HWND)lpParam;

    // 进入消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        switch (msg.message)
        {
        case WM_GRAPHICS:
            // 将绘图数据加入队列
            EnterCriticalSection(&cs);
            graphicsQueue.push(*(GRAPHICS *)msg.lParam);
            LeaveCriticalSection(&cs);
            break;

        default:
            DispatchMessage(&msg);
            break;
        }
    }

    // 销毁临界区
    DeleteCriticalSection(&cs);

    return 0;
}

// 初始化多线程绘图
void initgraph()
{
    // 创建绘图线程
    HANDLE hThread = CreateThread(NULL, 0, GraphicsThread, hWindow, 0, NULL);
    if (hThread == NULL)
    {
        MessageBox(NULL, "CreateThread failed", "Error", MB_OK);
        return;
    }

    // 设置线程优先级
    SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);

    // 关闭线程句柄
    CloseHandle(hThread);
}

接下来,实现绘制矩形的函数,该函数会将矩形的属性打包成一个GRAPHICS结构体,并通过队列发送给绘图线程。绘图线程会将队列中的数据取出并绘制出来。

// 绘制填充矩形
void fillrectangle(int x1, int y1, int x2, int y2)
{
    // 打包图形数据
    GRAPHICS graphics;
    graphics.lineColor = g_lineColor;
    graphics.fillColor = g_fillColor;
    graphics.lineWidth = g_lineWidth;
    graphics.x1 = x1;
    graphics.y1 = y1;
    graphics.x2 = x2;
    graphics.y2 = y2;

    // 将图形数据发送给绘图线程
    PostThreadMessage(dwGraphicsThreadId, WM_GRAPHICS, NULL, (LPARAM)&graphics);
}

在窗口初始化函数中,我们需要启动绘图线程,代码如下:

// 启动绘图线程
DWORD dwGraphicsThreadId;
HANDLE hGraphicsThread;
hGraphicsThread = CreateThread(NULL, 0, GraphicsThreadProc, NULL, 0, &dwGraphicsThreadId);

if (hGraphicsThread == NULL)
{
MessageBox(hwnd, TEXT("启动绘图线程失败!"), TEXT("错误"), MB_OK | MB_ICONERROR);
return 0;
}

// 关闭线程句柄
CloseHandle(hGraphicsThread);

在窗口消息处理函数中,我们需要处理绘图消息。当收到 WM_GRAPHICS 消息时,我们将从消息中获取的图形数据打包成 DRAW_GRAPHICS 结构体,并将其加入到绘图队列中。绘图线程会不断从队列中取出图形数据,并进行绘图。

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
// 启动绘图线程
DWORD dwGraphicsThreadId;
HANDLE hGraphicsThread;
hGraphicsThread = CreateThread(NULL, 0, GraphicsThreadProc, NULL, 0, &dwGraphicsThreadId);
 if (hGraphicsThread == NULL)
    {
        MessageBox(hwnd, TEXT("启动绘图线程失败!"), TEXT("错误"), MB_OK | MB_ICONERROR);
        return 0;
    }

    // 关闭线程句柄
    CloseHandle(hGraphicsThread);
    break;

case WM_PAINT:
    // 处理绘图
    fillrectangle(100, 100, 200, 200);
    break;

case WM_DESTROY:
    PostQuitMessage(0);
    break;

case WM_GRAPHICS:
    // 处理绘图消息
    DRAW_GRAPHICS* pDrawGraphics = new DRAW_GRAPHICS;
    pDrawGraphics->graphics = *(GRAPHICS*)lParam;
    g_graphicsQueue.push(pDrawGraphics);
    break;

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

return 0;
}

最后,我们需要实现绘图线程 GraphicsThreadProc。该线程会不断从绘图队列中取出图形数据,并进行绘图。

DWORD WINAPI GraphicsThreadProc(LPVOID lpParam)
{
UNREFERENCED_PARAMETER(lpParam);
// 创建画布
HDC hdc = GetDC(g_hWnd);
HDC hMemDC = CreateCompatibleDC(hdc);
HBITMAP hMemBmp = CreateCompatibleBitmap(hdc, g_width, g_height);
SelectObject(hMemDC, hMemBmp);

// 绘图循环
while (true)
{
    // 取出图形数据
    if (!g_graphicsQueue.empty())
    {
        DRAW_GRAPHICS* pDrawGraphics = g_graphicsQueue.front();
        g_graphicsQueue.pop();

        // 绘制图形
        setlinecolor(pDrawGraphics->graphics.lineColor);
        setfillcolor(pDrawGraphics->graphics.fillColor);
        setlinewidth(pDrawGraphics->graphics.lineWidth);
        fillrectangle(pDrawGraphics->graphics.x1, pDrawGraphics->graphics.y1,
            pDrawGraphics->graphics.x2, pDrawGraphics->graphics.y2);

        // 释放内存
delete pDrawGraphics;
}

// 检查是否需要退出循环
if (g_exitGraphicsThread)
{
    break;
}

// 休眠一段时间,以减小CPU占用率
Sleep(10);
}

// 释放资源
DeleteCriticalSection(&g_graphicsQueueCriticalSection);
CloseHandle(g_graphicsThreadHandle);

return 0;
}

这样,我们就实现了一个类似于easyx库的绘图功能。需要注意的是,这个实现只是一个简单的例子,还有很多地方需要完善,比如异常处理、线程同步等等。但是,通过这个例子,我们可以了解到多线程在实际应用中的重要性,以及如何利用多线程来提高程序的性能。

基于最新版ChatGPT4的回答,望采纳!!!有其他问题也可以询问我哦💕(最新版更智能,功能更加强大):
要实现类似easyx库的绘图效果,您需要使用Windows GDI(Graphics Device Interface)来进行绘制。下面是一个简单的示例代码,用于在创建的窗口中绘制一个矩形:

#include <windows.h>

// 保存窗口句柄
HWND hwnd;

// 窗口消息处理函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_CREATE:
            // 获取窗口句柄
            hwnd = hWnd;
            break;

        case WM_PAINT:
            {
                // 创建画笔
                HDC hdc = GetDC(hwnd);
                HPEN hPen = CreatePen(PS_SOLID, 2, RGB(255,0,0));
                HBRUSH hBrush = CreateSolidBrush(RGB(0,255,0));
                SelectObject(hdc, hPen);
                SelectObject(hdc, hBrush);

                // 绘制矩形
                RECT rect = {50, 50, 200, 150};
                Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);

                // 删除画笔
                DeleteObject(hPen);
                DeleteObject(hBrush);

                // 释放设备上下文
                ReleaseDC(hwnd, hdc);
            }
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }

    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    // 注册窗口类
    WNDCLASS wc = {0};
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = "MyWindowClass";
    RegisterClass(&wc);

    // 创建窗口
    HWND hWnd = CreateWindow("MyWindowClass", "My Window", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hInstance, NULL);

    if (!hWnd)
    {
        return -1;
    }

    // 显示窗口
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

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

    return msg.wParam;
}

在上面的代码中,我们通过创建画笔和刷子,然后使用SelectObject()函数将其选入设备上下文中。接着,我们可以使用GDI提供的函数进行绘图,如Rectangle()函数来绘制矩形。最后,我们删除画笔和刷子,并释放设备上下文。

您可以根据需要自定义函数,比如实现类似于EasyX中的setlinecolor()、setfillcolor()等函数。

不知道你这个问题是否已经解决, 如果还没有解决的话:

如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^该回答引用ChatGPT
要实现类似于easyx的绘图效果,需要使用Windows GDI(图形设备接口)来进行绘图。下面是一个简单的示例代码,可以实现绘制矩形的功能:
c
#include
// 定义画笔和画刷
HPEN hPen;
HBRUSH hBrush;
// 绘制矩形函数
void drawRectangle(HDC hdc, int x1, int y1, int x2, int y2) {
// 选择画笔和画刷
SelectObject(hdc, hPen);
SelectObject(hdc, hBrush);
// 绘制矩形
Rectangle(hdc, x1, y1, x2, y2);
}
// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE:
// 创建画笔和画刷
hPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
hBrush = CreateSolidBrush(RGB(0, 255, 0));
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// 绘制矩形
drawRectangle(hdc, 100, 100, 200, 200);
EndPaint(hwnd, &ps);
}
break;
case WM_DESTROY:
// 释放画笔和画刷
DeleteObject(hPen);
DeleteObject(hBrush);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 注册窗口类
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = "MyWindowClass";
RegisterClass(&wc);
// 创建窗口
HWND hwnd = CreateWindow("MyWindowClass", "My Window", WS_OVERLAPPEDWINDOW, 100, 100, 400, 300, NULL, NULL, hInstance, NULL);
// 显示窗口
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}

在上面的代码中,我们定义了一个`drawRectangle`函数,用于绘制矩形。该函数接受一个`HDC`参数,表示绘图设备的句柄,以及矩形的四个坐标值。在函数内部,我们先选择画笔和画刷,然后调用`Rectangle`函数绘制矩形。
在窗口过程函数中,我们在`WM_CREATE`消息中创建画笔和画刷,在`WM_PAINT`消息中调用`drawRectangle`函数绘制矩形,在`WM_DESTROY`消息中释放画笔和画刷。
最后,在`WinMain`函数中,我们注册窗口类,创建窗口,显示窗口,并进入消息循环。在消息循环中,我们调用`GetMessage`函数获取消息,并调用`TranslateMessage`和`DispatchMessage`函数处理消息。
当然,这只是一个简单的示例代码,如果要实现更复杂的绘图功能,还需要深入了解Windows GDI的相关知识。