http://www.cnblogs.com/Imageshop/archive/2012/12/13/2815712.html

在GDI+1.1的版本中,MS加入不少新的特性,其中的特效类Effect就是一个很有吸引力的东西,可惜在VS2010的Image类中,却没有把这个类封装进来(不晓得是不是我没有发现),这个也许MS也有自己的考虑的,毕竟要使用这些函数,必须要求系统是Windows Vista及其以上,而XP的市场占有率在那个时候还比较高的。  
       不过,作为一种选择,我们有义务把这些函数给哪些已经按照了这些最新系统的客户使用。
     
       其实,这些函数我在VB6下两年前就已经调用过,调用的方式也很简单明了,现在,在学习C#,就要考虑如何将他们封装入C#中。虽然哪些算法的更底层(像素级别的处理实现)实现在很早之前就已经实现,但是能够直接调用现有的函数对于不少朋友来说还是一件很幸福的事情的。

实现这个功能的第一步就是要找到这些函数的声明,这个在MSDN上有C风格的声明,改成C#语言的大部分都不成问题,参考 http://msdn.microsoft.com/en-us/library/ms533971(VS.85).aspx

例如,这个

  GpStatus WINGDIPAPI GdipBitmapApplyEffect(GpBitmap* bitmap, CGpEffect *effect, RECT *roi, BOOL useAuxData, VOID **auxData, INT *auxDataSize)

我写成这样:

      [DllImport( "gdiplus.dll",SetLastError = true, ExactSpelling = true,CharSet = CharSet.Unicode)]
private static extern int GdipBitmapApplyEffect(IntPtr bitmap, IntPtr effect, ref Rectangle rectOfInterest, bool useAuxData, IntPtr auxData, int auxDataSize);

对于第一个参数bitmap,你无法声明为C#的Bitmap类的,或者你也可以声明为HandleRef类型的,VS就是这么干的, 对于最后几个参数,是用来给用户返回一些数据,基本上不会有人对那几个数据感兴趣,因此你声不声明为out类型的参数也无所谓。

问题来了,第一个参数bitmap的本意是GDI+的image对象的句柄,在C#中,有Bitmap类,实际上我们知道他就是GDI+的封装,那么他的具体的实例中肯定也对应了一个GDI+对象的句柄,但是他封装的太厉害了,未给我们提供这个借口,这样一来,我们有两种选择,一是直接调用GDI+的加载图像的函数,得到对应的句柄,然后处理,然后调用GDI+的绘图API显示,但是这样无疑会增加工程量;二是我们强力爆破,寻找C#封装预留的后门,看能不能偷偷摸摸的得到这个句柄。呵呵,本人初学C#,还没这个火候,不过从高人哪些偷到一个代码,却是可以:

  /// <summary>
/// 获取对象的私有字段的值,感谢Aaron Lee Murgatroyd
/// </summary>
/// <typeparam name="TResult">字段的类型</typeparam>
/// <param name="obj">要从其中获取字段值的对象</param>
/// <param name="fieldName">字段的名称.</param>
/// <returns>字段的值</returns>
/// <exception cref="System.InvalidOperationException">无法找到该字段.</exception>
///
internal static TResult GetPrivateField<TResult>(this object obj, string fieldName)
{
if (obj == null) return default(TResult);
Type ltType = obj.GetType();
FieldInfo lfiFieldInfo = ltType.GetField( fieldName,System.Reflection.BindingFlags.GetField |System.Reflection.BindingFlags.Instance |System.Reflection.BindingFlags.NonPublic);
if (lfiFieldInfo != null)
return (TResult)lfiFieldInfo.GetValue(obj);
else
throw new InvalidOperationException(string.Format("Instance field '{0}' could not be located in object of type '{1}'.",fieldName, obj.GetType().FullName));
}

通过这个代码,如果你知道被封装的私有字段的名称,就可以获得该字段的值(原理我还看不懂)。
        好了,那我们如何知道C#封装的那个GDI+句柄的值呢,有办法,相信每个C#高手身边都会有个类似Refleator这样的工具吧,直接去看看Image类的实现吧。

以下是从代码中贴过来的:

  public static IntPtr NativeHandle(this Bitmap Bmp)
{
return Bmp.GetPrivateField<IntPtr>("nativeImage");
/* 用Reflector反编译System.Drawing.Dll可以看到Image类有如下的私有字段
internal IntPtr nativeImage;
private byte[] rawData;
private object userData;
然后还有一个 SetNativeImage函数
internal void SetNativeImage(IntPtr handle)
{
if (handle == IntPtr.Zero)
{
throw new ArgumentException(SR.GetString("NativeHandle0"), "handle");
}
this.nativeImage = handle;
}
这里在看看FromFile等等函数,其实也就是调用一些例如GdipLoadImageFromFile之类的GDIP函数,并把返回的GDIP图像句柄
通过调用SetNativeImage赋值给变量nativeImage,因此如果我们能获得该值,就可以调用VS2010暂时还没有封装的GDIP函数
进行相关处理了,并且由于.NET肯定已经初始化过了GDI+,我们也就无需在调用GdipStartup初始化他了。
*/
}

OK。万事大吉了,
       下面就是函数的调用了,比如高斯模糊的效果,就是几个函数的调用,多么简单啊。

 /// <summary>
/// 对图像进行高斯模糊,参考:http://msdn.microsoft.com/en-us/library/ms534057(v=vs.85).aspx
/// </summary>
/// <param name="Rect">需要模糊的区域,会对该值进行边界的修正并返回.</param>
/// <param name="Radius">指定高斯卷积核的半径,有效范围[0,255],半径越大,图像变得越模糊.</param>
/// <param name="ExpandEdge">指定是否对边界进行扩展,设置为True,在边缘处可获得较为柔和的效果. </param> public static void GaussianBlur(this Bitmap Bmp, ref Rectangle Rect, float Radius = 10, bool ExpandEdge = false)
{
int Result;
IntPtr BlurEffect;
BlurParameters BlurPara;
if ((Radius <0) || (Radius>255))
{
throw new ArgumentOutOfRangeException("半径必须在[0,255]范围内");
}
BlurPara.Radius = Radius ;
BlurPara.ExpandEdges = ExpandEdge;
Result = GdipCreateEffect(BlurEffectGuid, out BlurEffect);
if (Result == 0)
{
IntPtr Handle = Marshal.AllocHGlobal(Marshal.SizeOf(BlurPara));
Marshal.StructureToPtr(BlurPara, Handle, true);
GdipSetEffectParameters(BlurEffect, Handle, (uint)Marshal.SizeOf(BlurPara));
GdipBitmapApplyEffect(Bmp.NativeHandle(), BlurEffect, ref Rect, false, IntPtr.Zero, 0);
// 使用GdipBitmapCreateApplyEffect函数可以不改变原始的图像,而把模糊的结果写入到一个新的图像中
GdipDeleteEffect(BlurEffect);
Marshal.FreeHGlobal(Handle);
}
else
{
throw new ExternalException("不支持的GDI+版本,必须为GDI+1.1及以上版本,且操作系统要求为Win Vista及之后版本.");
}
}

  注意函数的第一个参数 this Bitmap Bmp,有了这个this,在你声明一个Bitmap类型变量后的只能提示里是不是有了这一项:

什么原理,我还没有学到哪一步,呵呵。

在实例代码中,我只提供了高斯模糊和USM锐化效果,其他的特效(色彩平衡、亮度对比度、红眼消除、色相饱和度、色阶、曲线等)大家查查MSDN模仿着也就写出来了,其实这里最重要的我认为还是高斯模糊,因为他是众多算法的基础,比如USM锐化就是基于高斯模糊的,所以他比高斯模糊的速度慢,还有比如高反差保留,Canny边缘算子,选区的羽化等等。

最后说一点图像滤镜的调整时的预览效果,预览时肯定要保留一份原始数据的,这个我还是倾向于直接用内存处理,最好不要经过类的封装的模式,大家看看代码可能就知道我说对的是什么意思了。

一个简单的UI效果:

代码下载地址:
  http://files.cnblogs.com/Imageshop/GdipEffect.rar

注意GDIP模糊的一个特性,模糊半径越大,所用的时间久越少,所以算法的优化是很重要的。

C#调用GDI+1.1中的函数实现高斯模糊、USM锐化等经典效果。的更多相关文章

  1. C语言学习_C如何在一个文件里调用另一个源文件中的函数

    问题 C如何在一个文件里调用另一个源文件中的函数,如题. 解决办法 当程序大了代码多了之后,想模块化开发,不同文件中存一点,是很好的解决办法,那我们如何做才能让各个文件中的代码协同工作呢?我们知道,m ...

  2. AngularJs调用NET MVC 控制器中的函数进行后台操作

    题目中提到的控制器指的是.NET  MVC的控制器,不是angularjs的控制器. 首先看主页面的代码: <!DOCTYPE html> <html> <head> ...

  3. 如何调用.so动态库中的函数,如何把自己的函数导出为.so的动态库函数供别人调用

    调用.so中的函数和平常的函数没有区别,只是在编译连接时加上-lxxxx就行了.要生成.so库,则编译时用下面的语句:gcc -shared -Wl,-soname,libmyfun.so -o li ...

  4. lua调用不同lua文件中的函数

    a.lua和b.lua在同一个目录下 a.lua调用b.lua中的test方法,注意b中test的写法 _M 和 a中调用方法: b.lua local _M = {}function _M.test ...

  5. C++中的函数模板

    我们在定义函数时,可以通过定义函数模板,来简化一些功能相同而数据类型不同的函数的定义和调用过程. C++中的函数模板 对于类的声明来说,也有同样的问题.有时,有两个或多个类,其功能是相同的,仅仅是数据 ...

  6. 编译器如何处理C++不同类中同名函数(参数类型个数都相同)

    转载请注明出处,版权归作者所有 lyzaily@126.com yanzhong.lee 作者按: 从这篇文章中,我们主要会认识到一下几点: 一.不类中的特征标相同的同名函数,它们是不同的函数,原因就 ...

  7. Oracle数据库中调用Java类开发存储过程、函数的方法

    Oracle数据库中调用Java类开发存储过程.函数的方法 时间:2014年12月24日  浏览:5538次 oracle数据库的开发非常灵活,不仅支持最基本的SQL,而且还提供了独有的PL/SQL, ...

  8. 在VS2012中采用C++中调用DLL中的函数 (4)

    这两天因为需要用到VS2012来生成一个DLL代码,但是之前并没有用过DLL相关的内容,从昨天开始尝试调试DLL的文件调用,起初笔者在网络上找到了3片采用VSXXX版本进行调试的例子,相关的内容见本人 ...

  9. 在C++中调用DLL中的函数 (3)

    1.dll的优点 代码复用是提高软件开发效率的重要途径.一般而言,只要某部分代码具有通用性,就可将它构造成相对独立的功能模块并在之后的项目中重复使用.比较常见的例子是各种应用程序框架,ATL.MFC等 ...

随机推荐

  1. vue Map 渲染DOM

    遍历对象(map),以键值对k:v的形式渲染DOM (1)DOM (2)数据模板

  2. 自动化运维工具saltstack02 -- 之SaltStack的配置管理

    SaltStack的配置管理 1.配置管理说明 配置管理,顾名思义及配置与管理, salt-master的配置文件编写格式之YAML语法说明: 数据的结构通过缩进来表示,每一级用两个空格来表示缩进,如 ...

  3. 从零开始的Python学习Episode 13——常用模块

    模块 一.time模块 时间戳(timestamp) :时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量. 元组(struct_time)   :struct_time元组共有9 ...

  4. 最短路径算法(I)

    弗洛伊德算法(Floyed-Warshall) 适用范围及时间复杂度 该算法的时间复杂度为O(N^3),适用于出现负边权的情况. 可以求取最短路径或判断路径是否连通.可用于求最小环,比较两点之间的大小 ...

  5. python2和python3同时存在如何安装和使用pip

    linux下 如果没有pip则需要安装pip python2安装pip sudo apt install python-pip1如果是python3,则如下: sudo apt install pyt ...

  6. 0917 词法分析程序(java版)

    1.运行结果: 2.源代码: package 词法分析;import java.util.Scanner;public class fenxi {public static void main(Str ...

  7. Scrum立会报告+燃尽图(十一月二十四日总第三十二次):视频剪辑

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2284 项目地址:https://git.coding.net/zhang ...

  8. Numpy and Pandas

    安装 视频链接:https://morvanzhou.github.io/tutorials/data-manipulation/np-pd/ pip install numpy pip instal ...

  9. 缓存-MemoryCache Class

    这是使用MemoryCache缓存的一个例子. private void btnGet_Click(object sender, EventArgs e) { ObjectCache cache = ...

  10. DS06--图

    一.学习总结 1.图的思维导图 2.图学习体会 深度优先遍历与广度优先遍历 不同点:广度优先搜索,适用于所有情况下的搜索,但是深度优先搜索不一定能适用于所有情况下的搜索.因为由于一个有解的问题树可能含 ...