如何在C语言中使用GDI+?

99ANYc3cd6
预计阅读时长 22 分钟
位置: 首页 C语言 正文

GDI+ 是 GDI (Graphics Device Interface) 的后继者,它为 Windows 应用程序提供了更强大、更丰富的二维图形功能,虽然 GDI+ 的原生 API 是 C++ 风格的,但它完全可以被 C 语言调用。

gdiplus C语言
(图片来源网络,侵删)

GDI+ 的核心概念

在使用 GDI+ 之前,你需要理解几个核心对象:

  1. GDI+ Startup (GDI+ 初始化):

    • GDI+ 不是一个标准的 Windows 库,你需要显式地初始化和关闭它。
    • 初始化通过 GdiplusStartup 函数完成,它会返回一个 GdiplusStartupInput 结构体和一个 ULONG_PTR 类型的 GdiplusToken,这个 Token 必须在程序结束时传给 GdiplusShutdown 来正确清理资源。
  2. Graphics (图形对象):

    • 这是 GDI+ 的核心,代表一个绘图表面,你通常用它来绘制线条、形状、文本和图像。
    • 它需要一个设备上下文作为构造参数,例如从窗口句柄 HWND 获取的 HDC
  3. Gdiplus::GdiplusBase (基类):

    gdiplus C语言
    (图片来源网络,侵删)
    • GDI+ 中的大多数类(如 Bitmap, Font, Pen, Brush)都继承自这个基类,它重载了 newdelete 操作符,使用内部引用计数来管理内存。
    • 关键点: 你不能直接用 C 的 free()delete 来释放这些对象,必须调用它们的 Dispose() 方法,或者在它们离开作用域时让析构函数自动释放。
  4. Pen (画笔):

    用于绘制线条、曲线和形状的轮廓,你可以定义它的颜色、宽度和样式(实线、虚线等)。

  5. Brush (画刷):

    • 用于填充形状(如矩形、椭圆)的内部,有多种画刷:
      • SolidBrush: 纯色填充。
      • LinearGradientBrush: 线性渐变填充。
      • TextureBrush: 使用图像进行纹理填充。
  6. Font (字体):

    用于定义文本的样式(字体名称、大小、粗细等)。

  7. Bitmap (位图):

    • 代表一个图像,你可以从文件加载,也可以在内存中创建,然后绘制到 Graphics 对象上。

C 语言环境设置

要在 C 语言项目中使用 GDI+,你需要做以下准备:

  1. 包含头文件:

    #include <windows.h>
    #include <gdiplus.h>
    // 使用 GDI+ 的命名空间,简化代码
    using namespace Gdiplus;
  2. 链接库文件: 你需要告诉链接器链接 gdiplus.lib

    • 在 Visual Studio 中:
      1. 右键点击你的项目 -> "属性" (Properties)。
      2. 转到 "配置属性" (Configuration Properties) -> "链接器" (Linker) -> "输入" (Input)。
      3. 在 "附加依赖项" (Additional Dependencies) 字段中,添加 gdiplus.lib
    • 在命令行编译时:
      cl your_code.c /link gdiplus.lib
  3. 运行时库: GDI+ 需要一个 DLL 文件 gdiplus.dll,这个文件在大多数现代 Windows 系统中都自带,如果你的程序需要在旧系统上运行,可能需要将此 DLL 打包到你的程序目录中。


完整 C 语言示例:在窗口上绘制图形

下面是一个完整的 C 语言示例,它创建一个简单的窗口,并在窗口上使用 GDI+ 绘制一个矩形、一个椭圆和一些文本。

main.c

#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
// 使用 GDI+ 命名空间
using namespace Gdiplus;
// GDI+ 全局变量
ULONG_PTR gdiplusToken;
// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    // 1. 初始化 GDI+
    GdiplusStartupInput gdiplusStartupInput;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    // 2. 注册窗口类
    const wchar_t CLASS_NAME[] = L"GDIPlus Demo Window";
    WNDCLASS wc = { };
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    RegisterClass(&wc);
    // 3. 创建窗口
    HWND hwnd = CreateWindowEx(
        0,                              // Optional window styles.
        CLASS_NAME,                     // Window class
        L"GDI+ in C Language",          // Window text
        WS_OVERLAPPEDWINDOW,            // Window style
        // Size and position
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
        NULL,       // Parent window
        NULL,       // Menu
        hInstance,  // Instance handle
        NULL        // Additional application data
    );
    if (hwnd == NULL) {
        return 0;
    }
    ShowWindow(hwnd, nCmdShow);
    // 4. 消息循环
    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    // 5. 关闭 GDI+
    GdiplusShutdown(gdiplusToken);
    return 0;
}
// 窗口过程
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            // 创建 GDI+ Graphics 对象
            // Graphics(hdc) 构造函数会使用传入的 HDC
            Graphics graphics(hdc);
            // 6. 绘制图形
            // a. 绘制一个红色矩形
            SolidBrush redBrush(Color(255, 255, 0, 0)); // ARGB: Alpha, Red, Green, Blue
            graphics.FillRectangle(&redBrush, 50, 50, 200, 100);
            // b. 绘制一个蓝色轮廓的椭圆
            Pen bluePen(Color(255, 0, 0, 255), 5); // 颜色, 宽度
            graphics.DrawEllipse(&bluePen, 100, 200, 250, 150);
            // c. 绘制一段文本
            FontFamily fontFamily(L"Arial");
            Font font(&fontFamily, 24, FontStyleRegular);
            SolidBrush blackBrush(Color(255, 0, 0, 0));
            graphics.DrawString(L"Hello GDI+ from C!", -1, &font, PointF(50.0f, 400.0f), &blackBrush);
            // 7. 释放 GDI+ 对象
            // GDI+ 对象通过析构函数自动释放,因为它们是栈上的对象。
            // 如果是用 new 创建的,则需要调用 delete。
            redBrush.Dispose();
            bluePen.Dispose();
            font.Dispose();
            blackBrush.Dispose();
            EndPaint(hwnd, &ps);
        }
        return 0;
        case WM_DESTROY: {
            PostQuitMessage(0);
        }
        return 0;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

如何编译和运行

  1. 使用 Visual Studio:

    • 创建一个新的 "Windows 桌面应用程序" 项目(选择 C++ 模板,但你可以把 .cpp 文件重命名为 .c)。
    • 将上面的代码复制到你的 .c 文件中。
    • 按照上面的步骤,在项目属性中链接 gdiplus.lib
    • 编译并运行。
  2. 使用 MinGW (gcc):

    • 将代码保存为 main.c
    • 打开命令行,运行以下命令:
      gcc main.c -o gdiplus_demo.exe -mwindows -lgdiplus
    • mwindows: 链接 Windows 子系统,避免出现控制台窗口。
    • lgdiplus: 链接 GDI+ 库。

运行后,你将看到一个窗口,里面有一个红色矩形、一个蓝色椭圆和一行黑色文本。


关键注意事项

  1. 内存管理:

    • 务必使用 Dispose(): 如果你使用 new 动态创建 GDI+ 对象(Pen* p = new Pen(...)),必须使用 delete p; 来释放,但最佳实践是创建在栈上,让它们在作用域结束时自动析构和释放,如示例所示。
    • 不要混用 GDI 和 GDI+: 尽量不要在同一个 HDC 上混用 GDI 函数(如 Rectangle)和 GDI+ 对象,GDI+ 会锁定 GDI 资源,混用可能导致死锁或不可预测的行为,一个常见的模式是:在 WM_PAINT 中获取 HDC,创建 Graphics 对象,完成所有 GDI+ 绘制,然后让 Graphics 和所有画笔/画刷对象离开作用域自动释放,最后调用 EndPaint
  2. 命名空间:

    • 在 C++ 中,using namespace Gdiplus; 是一个方便的写法,在纯 C 语言中,你不能使用 using,你必须使用完全限定名,Gdiplus::Graphics graphics(hdc);,GDI+ 的头文件是为 C++ 设计的,在 C 语言中直接使用可能会遇到一些兼容性问题,在实际的 C 语言项目中,最简单的方法还是将文件作为 C++ 编译(.cpp 扩展名),这样就可以使用 using 声明了。
  3. 错误处理:

    • GDI+ 函数通常会返回一个 Status 枚举值(如 Ok, GenericError, InvalidParameter),在实际应用中,你应该检查这些返回值以确保操作成功。

GDI+ 为 C 语言开发者提供了强大而灵活的 2D 图形能力,虽然其 API 设计偏向 C++,但通过正确的设置(链接库、包含头文件)和遵循其内存管理规则(使用 Dispose 或让对象自动析构),你就可以在 C 语言程序中充分利用 GDI+ 的功能来创建丰富的用户界面和图形应用。

-- 展开阅读全文 --
头像
C语言如何判断两个字符串是否为变位词?
« 上一篇 04-12
extern在C语言中如何正确使用?
下一篇 » 04-12

相关文章

取消
微信二维码
支付宝二维码

目录[+]