在使用 C# 进行图像处理时,若需快速实现高斯模糊且不修改原始图像,可通过调用 GDI+ 的底层接口完成。本文介绍一种通过 `GdipBitmapCreateApplyEffect` 实现非破坏性模糊处理的方法,其核心在于正确传递句柄与结果回收。
该方法利用 GDI+ 提供的特效接口,将原图作为输入源,生成一个新图像对象,避免直接修改原始数据。关键点在于:函数执行后返回的是指向新图像的指针(IntPtr),必须通过特定方式将其转换为 .NET 可识别的
Bitmap 实例。
以下为完整实现逻辑:
1 ///
2 /// 对指定区域应用高斯模糊效果,并返回新图像
3 ///
4 /// 原始图像
5 /// 处理区域
6 /// 模糊半径(0-255)
7 /// 是否扩展边缘像素
8 /// 处理后的图像副本
9 public static Bitmap ApplyGaussianBlur(this Bitmap source, Rectangle region, float blurRadius = 10, bool extendEdges = false)
10 {
11 var outputImage = new Bitmap(source);
12
13 IntPtr effectHandle;
14 var parameters = new BlurParameters { Radius = blurRadius, ExpandEdges = extendEdges };
15
16 if (blurRadius < 0 || blurRadius > 255)
17 throw new ArgumentOutOfRangeException("模糊半径必须在 [0, 255] 范围内");
18
19 // 创建高斯模糊效果实例
20 int result = GdipCreateEffect(BlurEffectGuid, out effectHandle);
21 if (result != 0)
22 throw new ExternalException("当前系统不支持 GDI+ 1.1 以上版本,或操作系统不满足要求");
23
24 // 将参数结构体写入内存
25 IntPtr paramPtr = Marshal.AllocHGlobal(Marshal.SizeOf(parameters));
26 Marshal.StructureToPtr(parameters, paramPtr, true);
27
28 // 设置效果参数
29 GdipSetEffectParameters(effectHandle, paramPtr, (uint)Marshal.SizeOf(parameters));
30
31 // 获取源图像句柄和目标区域
32 IntPtr srcHandle = source.NativeHandle();
33 IntPtr destHandle = IntPtr.Zero;
34
35 // 执行模糊处理,生成新图像
36 int status = GdipBitmapCreateApplyEffect(
37 ref srcHandle,
38 1,
39 effectHandle,
40 ref region,
41 ref region,
42 out destHandle,
43 false,
44 IntPtr.Zero,
45 0
46 );
47
48 // 成功则从句柄还原为 Bitmap
49 if (status == 0 && destHandle != IntPtr.Zero)
50 {
51 outputImage = destHandle.NativeBitmapPtrToBitmap();
52 }
53
54 // 清理资源
55 GdipDeleteEffect(effectHandle);
56 Marshal.FreeHGlobal(paramPtr);
57
58 return outputImage;
59 }
其中两个辅助方法用于从原生句柄恢复 .NET 图像对象:
1 ///
2 /// 从 GDI+ 原生句柄创建 Bitmap 实例
3 ///
4 /// 原生图像句柄
5 /// 对应的 Bitmap
6 public static Bitmap NativeBitmapPtrToBitmap(this IntPtr handle)
7 {
8 return typeof(Bitmap).InvokeStaticPrivateMethod("FromGDIplus", handle);
9 }
10
11 ///
12 /// 调用类型中的私有静态方法
13 ///
14 /// 返回类型
15 /// 目标类型
16 /// 方法名
17 /// 参数列表
18 /// 方法返回值
19 public static TResult InvokeStaticPrivateMethod(this Type type, string methodName, params object[] args)
20 {
21 var method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.NonPublic);
22 if (method == null)
23 throw new InvalidOperationException($"无法找到私有静态方法: {methodName} in {type.FullName}");
24
25 return (TResult)method.Invoke(null, args);
26 }
此方案的优势在于:
- 不改变原始图像;
- 支持局部区域处理;
- 利用系统级优化,性能较高;
- 可扩展至其他 GDI+ 特效。
注意:使用完毕后应确保释放返回的图像资源,防止内存泄漏。建议结合 `IDisposable` 或 `using` 语句管理生命周期。
参考文档:[MSDN - GdipBitmapCreateApplyEffect](https://learn.microsoft.com/en-us/windows/win32/api/gdiplus/nc-gdiplus-gpbitmapcreateapplyeffect)