C语言如何调用GDIplus实现图形绘制?

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

GDI+ (Graphics Device Interface Plus) 是微软提供的图形设备接口的升级版本,它提供了更强大、更易用的 2D 图形、成像和字体功能,虽然 GDI+ 的原生 API 是为 C++ 设计的,但它也完全可以在 C 语言中使用。

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

下面我将从核心概念、环境搭建、基本用法到高级应用,为你提供一个全面的指南。


核心概念

在开始编码之前,理解 GDI+ 的几个核心概念至关重要:

  1. GDI+ 初始化和终止

    • GDI+ 是一个 COM 组件,不能直接调用,你必须先初始化它,使用完毕后必须终止它,否则会导致内存泄漏和程序不稳定。
    • GdiplusStartup: 初始化 GDI+ 环境。
    • GdiplusShutdown: 清理并终止 GDI+ 环境。
  2. GDI+ 对象

    c语言 gdiplus
    (图片来源网络,侵删)
    • GDI+ 中的图形元素(如画笔、画刷、字体、图像)都是通过结构体来表示的,Gdiplus::PenGdiplus::SolidBrush
    • 这些对象在使用时都需要创建,使用完毕后必须销毁,以释放它们占用的资源,这是 C 语言中使用 GDI+ 最容易出错的地方。
  3. Graphics 对象

    • 这是所有绘图操作的基础,它代表一个可绘制的表面,比如一个窗口的客户端区域、一个打印机页面或一个位图图像。
    • 你需要从设备上下文(HDC)创建一个 Graphics 对象,然后所有的绘图方法(如画线、画矩形)都通过这个 Graphics 对象来调用。
  4. 句柄

    • 在 C 语言中,我们主要与 Windows API 交互,所以我们会频繁使用 Windows 句柄,如 HWND (窗口句柄) 和 HDC (设备上下文句柄),GDI+ 的许多函数都需要这些句柄作为输入。

环境搭建

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

  1. 包含头文件: 在你的 C 源文件中,包含 GDI+ 的头文件,你需要包含 gdiplus.h,并且为了使用 GDI+ 的常量(如 Color::Red),还需要包含 gdipluscolor.h

    c语言 gdiplus
    (图片来源网络,侵删)
    #include <windows.h>
    #include <gdiplus.h>
    #include <gdipluscolor.h> // 用于颜色常量
  2. 链接 GDI+ 库: 你需要告诉链接器链接 GDI+ 的库文件 gdiplus.lib

    • 在 Visual Studio 中: 右键点击你的项目 -> 属性 -> 配置属性 -> 链接器 -> 输入 -> 附加依赖项,在其中添加 gdiplus.lib

    • 在命令行编译时: 使用 /link 选项:cl your_code.c /link gdiplus.lib

  3. 定义 GDI+ 初始化结构体GdiplusStartup 函数需要一个 GdiplusStartupInput 结构体和一个 GdiplusStartupOutput 结构体(后者通常为 NULL),我们通常在全局或函数作用域定义一个 ULONG_PTR 类型的 token 变量来存储 GDI+ 的令牌。


C 语言中使用 GDI+ 的基本步骤

下面是一个完整的、最简单的示例,它创建一个窗口并在窗口上绘制一个红色的矩形,这个例子涵盖了所有核心步骤。

示例代码:绘制一个红色矩形

#include <windows.h>
#include <gdiplus.h>
#include <gdipluscolor.h>
#include <stdio.h>
// 必须在全局或函数作用域定义
ULONG_PTR gdiplusToken;
// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            // 1. 从 HDC 创建 Graphics 对象
            Gdiplus::Graphics graphics(hdc);
            // 2. 创建一个画刷 (SolidBrush)
            Gdiplus::SolidBrush brush(Gdiplus::Color::Red);
            // 3. 使用 Graphics 对象和画刷绘制一个矩形
            // (x, y, width, height)
            graphics.FillRectangle(&brush, 50, 50, 200, 100);
            // 4. 销毁创建的对象(非常重要!)
            // 注意:Gdiplus::Graphics 对象在超出作用域时会自动销毁,
            // 但显式调用 DeleteObject 是一个好习惯,尤其是在复杂代码中。
            // 对于由 GDI+ 内部管理的对象(如从HDC创建的Graphics),通常不需要手动删除。
            // 但对于我们自己创建的Pen, Brush等,必须删除。
            delete &brush;
            EndPaint(hwnd, &ps);
            break;
        }
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    // --- 1. 初始化 GDI+ ---
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    // --- 2. 注册窗口类 ---
    const wchar_t CLASS_NAME[] = L"GDIPlus Window Class";
    WNDCLASS wc = { };
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    RegisterClass(&wc);
    // --- 3. 创建窗口 ---
    HWND hwnd = CreateWindowEx(
        0,                              // Optional window styles.
        CLASS_NAME,                     // Window class
        L"GDI+ C Language Demo",        // Window text
        WS_OVERLAPPEDWINDOW,            // Window style
        CW_USEDEFAULT, CW_USEDEFAULT,   // Position (x, y)
        800, 600,                       // Size (width, height)
        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+ (在程序退出前) ---
    Gdiplus::GdiplusShutdown(gdiplusToken);
    return 0;
}

代码解析:

  1. WinMain 中的初始化与终止

    • 在程序入口 WinMain 的最开始,我们调用 GdiplusStartup 来“启动”GDI+。
    • 在消息循环结束后,程序即将退出时,我们调用 GdiplusShutdown 来“关闭”GDI+,释放所有资源。
  2. WM_PAINT 消息处理

    • 当窗口需要重绘时(窗口被遮挡后又显示出来),系统会发送 WM_PAINT 消息。
    • BeginPaint 获取一个设备上下文 HDC,这是进行 GDI 绘图的基础。
    • Gdiplus::Graphics graphics(hdc);:这是连接 GDI 和 GDI+ 的桥梁,我们用 HDC 创建了一个 Graphics 对象,后续所有 GDI+ 的绘图操作都通过这个 graphics 对象进行。
    • Gdiplus::SolidBrush brush(Gdiplus::Color::Red);:创建一个红色的实心画刷,注意,这是一个 C++ 风格的对象,在 C 语言中,我们同样可以创建和使用它。
    • graphics.FillRectangle(&brush, ...);:调用 Graphics 对象的 FillRectangle 方法,使用我们创建的画刷来绘制一个填充矩形。
    • delete &brush;至关重要! 我们手动创建的 SolidBrush 对象,在使用完毕后必须用 delete 销毁,否则会造成内存泄漏。Graphics 对象通常由 HDC 创建,其生命周期由 GDI+ 管理,一般不需要手动 delete

常用 GDI+ 绘图操作

掌握了基本流程后,你可以尝试更多的绘图操作。

绘制线条

// 在 WM_PAINT 中
Gdiplus::Graphics graphics(hdc);
Gdiplus::Pen pen(Gdiplus::Color::Blue, 5.0f); // 颜色为蓝色,宽度为5
graphics.DrawLine(&pen, 10, 10, 300, 300);
delete &pen; // 记得删除

绘制文本

// 在 WM_PAINT 中
Gdiplus::Graphics graphics(hdc);
Gdiplus::FontFamily fontFamily(L"Arial");
Gdiplus::Font font(&fontFamily, 24, Gdiplus::FontStyleRegular, Gdiplus::UnitPoint);
Gdiplus::SolidBrush brush(Gdiplus::Color::Black);
graphics.DrawString(L"Hello GDI+!", -1, &font, Gdiplus::PointF(20, 150), &brush);
delete &font;
delete &brush;

加载并绘制图像

// 在 WM_PAINT 中
Gdiplus::Graphics graphics(hdc);
// 注意:L"image.png" 是宽字符串,文件路径要正确
Gdiplus::Image image(L"image.png"); 
if (image.GetLastStatus() == Gdiplus::Ok) {
    graphics.DrawImage(&image, 0, 0, image.GetWidth(), image.GetHeight());
}
// Image 对象也需要删除
delete &image;

C 语言与 C++ 的差异及注意事项

在 C 语言中使用 GDI+,你实际上是在调用一个 C++ 编写的库,这带来了一些需要注意的地方:

  1. 对象生命周期管理

    • 这是最关键的一点! 任何通过 new 或 GDI+ 提供的工厂函数创建的对象(如 Pen, Brush, Font, Image),你都必须负责在不再使用时用 delete 将其销毁,忘记 delete 会导致内存泄漏。
    • Graphics 这种从 Windows 句柄(HDC)创建的对象,其生命周期由 GDI+ 内部管理,通常不需要手动 delete
  2. 命名空间

    • GDI+ 的所有类和函数都在 Gdiplus 命名空间下,在 C++ 中,你可以用 using namespace Gdiplus; 来简化,但在 C 语言中,你必须每次都完整地写出 Gdiplus::Gdiplus::Graphics
  3. 字符串处理

    • GDI+ 的字符串函数(如 DrawString)和文件路径(如 Image 构造函数)通常使用宽字符(wchar_t),即 L"..." 格式的字符串,在处理 Windows 路径时,确保使用正确的宽字符 API。

在 C 语言中使用 GDI+ 是完全可行的,并且功能非常强大,核心在于:

  1. 正确初始化和终止GdiplusStartupGdiplusShutdown 必须成对出现。
  2. 正确管理对象生命周期:创建的 Pen, Brush, Font, Image 等对象,用完后必须 delete
  3. 理解 Graphics 对象的作用:它是所有绘图操作的载体,由 HDC 创建。
  4. 处理宽字符:注意字符串和路径的格式。

虽然 C++ 的封装让 GDI+ 的使用更安全(RAII 可以自动管理对象生命周期),但在 C 语言中,只要严格遵守上述规则,你依然可以高效、稳定地利用 GDI+ 的强大功能来创建复杂的图形界面。

-- 展开阅读全文 --
头像
C语言资源如何高效获取与利用?
« 上一篇 04-17
百度地图如何编织现实世界的数字梦境?
下一篇 » 04-17

相关文章

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

目录[+]