以下是使用GDI+在C++和C#中创建PNG图片并写入旋转30度文本的实现对比:

特性 C++实现 C#实现
代码量 约50行 约15行
初始化 需要显式初始化GDI+环境 自动处理GDI+初始化
核心类 Gdiplus::BitmapGdiplus::Graphics System.Drawing.BitmapSystem.Drawing.Graphics
旋转实现 使用Matrix类或RotateTransform 直接使用RotateTransform方法
保存格式 需要获取PNG编码器CLSID 直接指定保存格式为PNG
资源释放 需要手动释放GDI+对象 可使用using语句自动释放

C#画图,只要9行代码,VC++要50行代码

VC++纯api调用gdiplus.dll要100行左右的代码

using System.Drawing;
using System.Drawing.Imaging;

class Program {
    static void Main() {
        // 创建位图
        using(var bitmap = new Bitmap(400, 200)) {
            using(var graphics = Graphics.FromImage(bitmap)) {
                // 设置背景色
                graphics.Clear(Color.White);
                
                // 设置文本属性
                var font = new Font("Arial", 24);
                var brush = new SolidBrush(Color.Black);
                
                // 旋转并绘制文本
                graphics.TranslateTransform(100, 50);
                graphics.RotateTransform(30);
                graphics.DrawString("helloworld", font, brush, 0, 0);
            }
            // 保存为PNG
            bitmap.Save("output.png", ImageFormat.Png);
        }
    }
}

关键差异分析

  1. 初始化过程‌:

    • C++需要显式初始化和清理GDI+环境
    • C#自动处理GDI+的初始化和资源管理
  2. 资源管理‌:

    • C++需要手动释放所有GDI+对象
    • C#可利用using语句自动释放资源
  3. 图像保存‌:

    • C++需要获取PNG编码器的CLSID
    • C#直接使用ImageFormat.Png枚举值
  4. 代码复杂度‌:

    • C++实现需要更多样板代码
    • C#语法更简洁,框架提供了更多便利方法
  5. 旋转实现‌:

    • 两者都使用RotateTransform方法
    • C#的API设计更加直观易用

适用场景建议

  • 选择C++‌:当需要最高性能、直接系统级访问或与现有C++代码库集成时
  • 选择C#‌:当开发效率、代码简洁性和快速原型开发是首要考虑因素时

两种语言都能很好地完成GDI+图像处理任务,但C#的实现通常更为简洁高效,特别是在.NET环境下开发时。

VC++用GDIPLUS库画图,50行代码

#include <windows.h>
#include <gdiplus.h>
#include <iostream>
using namespace Gdiplus;

int main() {
    // 初始化GDI+
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    // 创建位图
    Bitmap bitmap(400, 200, PixelFormat32bppARGB);
    Graphics graphics(&bitmap);
    
    // 设置背景色
    graphics.Clear(Color(255, 255, 255, 255));
    
    // 设置文本属性
    Font font(L"Arial", 24);
    SolidBrush brush(Color(255, 0, 0, 0));
    PointF point(100.0f, 50.0f);
    
    // 旋转文本30度
    graphics.TranslateTransform(point.X, point.Y);
    graphics.RotateTransform(30.0f);
    graphics.DrawString(L"helloworld", -1, &font, PointF(0, 0), &brush);
    graphics.ResetTransform();
    
    // 保存为PNG
    CLSID pngClsid;
    GetEncoderClsid(L"image/png", &pngClsid);
    bitmap.Save(L"output.png", &pngClsid, NULL);
    
    // 清理GDI+
    GdiplusShutdown(gdiplusToken);
    return 0;
}

// 获取编码器CLSID
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) {
    UINT num = 0, size = 0;
    GetImageEncodersSize(&num, &size);
    if(size == 0) return -1;
    
    ImageCodecInfo* pImageCodecInfo = (ImageCodecInfo*)malloc(size);
    GetImageEncoders(num, size, pImageCodecInfo);
    
    for(UINT i = 0; i < num; i++) {
        if(wcscmp(pImageCodecInfo[i].MimeType, format) == 0) {
            *pClsid = pImageCodecInfo[i].Clsid;
            free(pImageCodecInfo);
            return i;
        }
    }
    free(pImageCodecInfo);
    return -1;
}

VC++纯api调用gdiplus.dll要100行左右的代码


#include <windows.h>
#include <objidl.h>
#include <stdio.h>

// 手动定义GDI+结构体
typedef struct {
    UINT32 GdiplusVersion;
    void* DebugEventCallback;
    BOOL SuppressBackgroundThread;
    BOOL SuppressExternalCodecs;
} GdiplusStartupInput;

// 声明函数指针类型
typedef int (WINAPI* GdiplusStartupFunc)(ULONG_PTR*, const GdiplusStartupInput*, void*);
typedef void (WINAPI* GdiplusShutdownFunc)(ULONG_PTR);
typedef int (WINAPI* GdipCreateBitmapFromScan0Func)(int, int, int, int, void*, void**);
typedef int (WINAPI* GdipGetImageGraphicsContextFunc)(void*, void**);
typedef int (WINAPI* GdipGraphicsClearFunc)(void*, DWORD);
typedef int (WINAPI* GdipCreateFontFunc)(void*, float, int, int, void**);
typedef int (WINAPI* GdipCreateSolidFillFunc)(DWORD, void**);
typedef int (WINAPI* GdipSetTextRenderingHintFunc)(void*, int);
typedef int (WINAPI* GdipDrawStringFunc)(void*, const WCHAR*, int, void*, void*, void*, void*);
typedef int (WINAPI* GdipSaveImageToFileFunc)(void*, const WCHAR*, void*, void*);
typedef int (WINAPI* GdipDeleteGraphicsFunc)(void*);
typedef int (WINAPI* GdipDeleteFontFunc)(void*);
typedef int (WINAPI* GdipDeleteBrushFunc)(void*);
typedef int (WINAPI* GdipDeleteBitmapFunc)(void*);
typedef int (WINAPI* GdipRotateWorldTransformFunc)(void*, float, int);
typedef int (WINAPI* GdipTranslateWorldTransformFunc)(void*, float, float, int);

int main() {
    HMODULE hGdiplus = LoadLibrary(L"gdiplus.dll");
    if (!hGdiplus) return 1;

    // 获取函数地址
    GdiplusStartupFunc GdiplusStartup = (GdiplusStartupFunc)GetProcAddress(hGdiplus, "GdiplusStartup");
    GdiplusShutdownFunc GdiplusShutdown = (GdiplusShutdownFunc)GetProcAddress(hGdiplus, "GdiplusShutdown");
    GdipCreateBitmapFromScan0Func GdipCreateBitmapFromScan0 = (GdipCreateBitmapFromScan0Func)GetProcAddress(hGdiplus, "GdipCreateBitmapFromScan0");
    GdipGetImageGraphicsContextFunc GdipGetImageGraphicsContext = (GdipGetImageGraphicsContextFunc)GetProcAddress(hGdiplus, "GdipGetImageGraphicsContext");
    GdipGraphicsClearFunc GdipGraphicsClear = (GdipGraphicsClearFunc)GetProcAddress(hGdiplus, "GdipGraphicsClear");
    GdipCreateFontFunc GdipCreateFont = (GdipCreateFontFunc)GetProcAddress(hGdiplus, "GdipCreateFont");
    GdipCreateSolidFillFunc GdipCreateSolidFill = (GdipCreateSolidFillFunc)GetProcAddress(hGdiplus, "GdipCreateSolidFill");
    GdipSetTextRenderingHintFunc GdipSetTextRenderingHint = (GdipSetTextRenderingHintFunc)GetProcAddress(hGdiplus, "GdipSetTextRenderingHint");
    GdipDrawStringFunc GdipDrawString = (GdipDrawStringFunc)GetProcAddress(hGdiplus, "GdipDrawString");
    GdipSaveImageToFileFunc GdipSaveImageToFile = (GdipSaveImageToFileFunc)GetProcAddress(hGdiplus, "GdipSaveImageToFile");
    GdipDeleteGraphicsFunc GdipDeleteGraphics = (GdipDeleteGraphicsFunc)GetProcAddress(hGdiplus, "GdipDeleteGraphics");
    GdipDeleteFontFunc GdipDeleteFont = (GdipDeleteFontFunc)GetProcAddress(hGdiplus, "GdipDeleteFont");
    GdipDeleteBrushFunc GdipDeleteBrush = (GdipDeleteBrushFunc)GetProcAddress(hGdiplus, "GdipDeleteBrush");
    GdipDeleteBitmapFunc GdipDeleteBitmap = (GdipDeleteBitmapFunc)GetProcAddress(hGdiplus, "GdipDeleteBitmap");
    GdipRotateWorldTransformFunc GdipRotateWorldTransform = (GdipRotateWorldTransformFunc)GetProcAddress(hGdiplus, "GdipRotateWorldTransform");
    GdipTranslateWorldTransformFunc GdipTranslateWorldTransform = (GdipTranslateWorldTransformFunc)GetProcAddress(hGdiplus, "GdipTranslateWorldTransform");

    // 初始化GDI+
    GdiplusStartupInput gdiplusStartupInput = {1};
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    // 创建位图(400x200 ARGB格式)
    void* bitmap;
    GdipCreateBitmapFromScan0(400, 200, 0, 0x26200A, NULL, &bitmap);

    // 获取图形上下文
    void* graphics;
    GdipGetImageGraphicsContext(bitmap, &graphics);

    // 设置白色背景
    GdipGraphicsClear(graphics, 0xFFFFFFFF);

    // 创建字体和画刷
    void* font;
    GdipCreateFont(L"Arial", 24, 0, 0, &font);
    void* brush;
    GdipCreateSolidFill(0xFF000000, &brush);

    // 设置文本渲染质量
    GdipSetTextRenderingHint(graphics, 4); // AntiAliasGridFit

    // 旋转30度并绘制文本
    GdipTranslateWorldTransform(graphics, 100, 50, 0);
    GdipRotateWorldTransform(graphics, 30.0f, 0);
    GdipDrawString(graphics, L"helloworld", -1, font, NULL, brush, NULL);

    // 保存为PNG
    CLSID pngClsid = {0x557cf406, 0x1a04, 0x11d3, {0x9a, 0x73, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e}};
    GdipSaveImageToFile(bitmap, L"output.png", &pngClsid, NULL);

    // 释放资源
    GdipDeleteGraphics(graphics);
    GdipDeleteFont(font);
    GdipDeleteBrush(brush);
    GdipDeleteBitmap(bitmap);
    GdiplusShutdown(gdiplusToken);
    FreeLibrary(hGdiplus);
    return 0;
}

以上是直接调用gdiplus.dll实现PNG创建与旋转文本的纯API方案,基于用户历史需求(C++实现)进行扩展:

核心实现原理

通过LoadLibrary/GetProcAddress动态加载gdiplus.dll,手动声明所有必需的函数原型和结构体,完全绕过GDI+头文件依赖。关键步骤包括:

  1. 动态加载gdiplus.dll并获取函数地址
  2. 手动定义GDI+所需的结构体(如GdiplusStartupInput)
  3. 通过函数指针调用GDI+接口

关键实现要点

  1. 动态加载机制‌:通过LoadLibrary/GetProcAddress实现运行时绑定,避免静态链接
  2. 结构体手动定义‌:完全重现GdiplusStartupInput等核心结构体
  3. PNG编码器CLSID‌:硬编码PNG编码器的类标识符(0x557cf406...)避免额外查询
  4. 资源管理‌:严格遵循创建/销毁配对原则,防止内存泄漏
  5. 坐标系变换‌:通过Translate/Rotate组合实现精确的文本旋转定位

对比原GDI+库方案

特性 纯API方案 GDI+库方案
依赖项 仅需gdiplus.dll 需要gdiplus.lib和头文件
初始化 手动定义所有结构体 自动包含预定义结构体
函数调用 通过函数指针间接调用 直接方法调用
代码量 显著增加(需声明所有接口) 简洁
灵活性 可选择性加载所需函数 必须链接完整库
维护性 需自行跟踪接口变更 微软官方维护

该方案特别适用于需要严格控制依赖或进行GDI+功能裁剪的场景,但需注意所有GDI+接口均需手动声明,开发复杂度较高

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐