.net中获取图像缩略图的函数GetThumbnailImage
关于.net中获取图像缩略图的函数GetThumbnailImage的一些认识。
在很多图像软件中,打开一幅图像的时候都会显示其缩略图,在看图软件中这样的需求更为常见。如何快速的获取缩略图的信息并提供给用户查看,是个值得研究的问题。在我所研究过的图像格式中,只有JPG和PSD两种格式可能内嵌了图像自身的缩略图信息。
在.net中,图像处理方面的内容主要是借助于GDI+的平板化API函数实现的。为了获取GDI+能支持的那几种格式的缩略图,可以调用Bitmap或者Image类的GetThumbnailImage函数。用Reflecor反编译后知道,这个函数的主要实现代码如下所示:

- public Image GetThumbnailImage(int thumbWidth, int thumbHeight, GetThumbnailImageAbort callback, IntPtr callbackData)
- {
- IntPtr zero = IntPtr.Zero;
- int status = SafeNativeMethods.Gdip.GdipGetImageThumbnail(new HandleRef(this, this.nativeImage), thumbWidth, thumbHeight, out zero, callback, callbackData);
- if (status != 0)
- {
- throw SafeNativeMethods.Gdip.StatusException(status);
- }
- return CreateImageObject(zero);
- }

那么其实他也是直接调用GdipGetImageThumbnail函数。
下面我们主要通过实验说说这个函数的实质和其可应用的场合以及不应该应用的场合。
为了测试公平,我们选用VB6作为测试语言,这有两个原因:(1)因为VB6直接调用GDI+的API函数很方便,也可以降低.net中创建各种对象所用的时间。(2)我在C#中调用Bitmap.FromFile读取文件的时间比VB6中使用同样的API要慢很多,不知道为什么。
结论1: 该函数首先判断图像是否内嵌了缩略图,如果有,则直接读取他,然后再将获得的缩略图缩放到用户调用时指定的大小。如果没有,则从图像数据中抽样填充到缩略图数据中,至于抽样算法,这个没有研究,也许是线性插值吧。
验证前的准备工作:
(1) 一副4000*3000的数码照片,JPG格式,内嵌了缩略图信息(如何验证:可以用很多查看EXIF信息的软件查看),可在此处下载。
(2) 一副4000*3000的照片,JPG格式,没有内嵌缩略图信息(如何验证:可以用很多查看EXIF信息的软件查看),可在此处下载。
(3) 一副4000*3000的照片,Png格式,由于无法上传大于5MB的文件,请朋友自行用工具转换。
对上述三幅图像进行获取缩略图的操作,具体代码如下:

- '第一步:加载图像
- Elapse = GetTickCount
- GdipLoadImageFromFile StrPtr(FileName), Bitmap
- Result = Result + "加载图像用时: " & GetTickCount - Elapse & " 毫秒。" + vbCrLf
- GdipGetImageWidth Bitmap, Width
- GdipGetImageHeight Bitmap, Height
- GetFitSize Width, Height, 600, 450, FitX, FitY, FitWidth, FitHeight
- '第二步:获取缩略图
- Elapse = GetTickCount
- GdipGetImageThumbnail Bitmap, FitWidth, FitHeight, Thumb
- Result = Result + "获取缩略图用时: " & GetTickCount - Elapse & " 毫秒。" + vbCrLf
- GdipCreateFromHDC Hdc, Graphics
- '第三步:绘制缩略图
- Elapse = GetTickCount
- GdipDrawImageRect Graphics, Thumb, FitX, FitY, FitWidth, FitHeight
- Result = Result + "绘制缩略图: " & GetTickCount - Elapse & " 毫秒。" + vbCrLf
- GdipDisposeImage Bitmap
- GdipDisposeImage Thumb
- GdipDeleteGraphics Graphics

我们依次查看结果:
图1 : 内嵌了缩略图的JPG图像
图2: 未内嵌缩略图的JPG图像
图3: PNG图像
上述缩略图的大小设置为600*450。
通过上面3个测试结果图的比较,可以明显看到: 内嵌了缩略图的JPG图像获得最后的缩略图很模糊,但是速度相当的块,而未内嵌了缩略图的JPG图像以及PNG图像获得的缩略图非常的清晰,但是耗时很多。因此我们可以初步的判断如果内嵌了缩略图,则GdipGetImageThumbnail会直接从内嵌的数据中进行缩放。为了进一步验证这一点,我生成了一副缩略图和原图完全不配套的JPG图像,来验证这一点,可从此处下载:
处理结果如下图:
可见,执行速度还是不错的,缩略图的结果却是错误的,但是和我们嵌入的缩略图却是一致的。
附带说一个问题:不知道大家注意到没有,上述代码中 GdipLoadImageFromFile 函数执行的时间都很短,而基本相同的函数在C#的Bitmap.FromFile函数中对于上述测试图像都要200多ms,不知为什么,附上Bitmap.FromFile的源码:
- public static Image FromFile(string filename, bool useEmbeddedColorManagement)
- {
- int num;
- if (!File.Exists(filename))
- {
- IntSecurity.DemandReadFileIO(filename);
- throw new FileNotFoundException(filename);
- }
- filename = Path.GetFullPath(filename);
- IntPtr zero = IntPtr.Zero;
- if (useEmbeddedColorManagement)
- {
- num = SafeNativeMethods.Gdip.GdipLoadImageFromFileICM(filename, out zero);
- }
- else
- {
- num = SafeNativeMethods.Gdip.GdipLoadImageFromFile(filename, out zero);
- }
- if (num != 0)
- {
- throw SafeNativeMethods.Gdip.StatusException(num);
- }
- num = SafeNativeMethods.Gdip.GdipImageForceValidation(new HandleRef(null, zero));
- if (num != 0)
- {
- SafeNativeMethods.Gdip.GdipDisposeImage(new HandleRef(null, zero));
- throw SafeNativeMethods.Gdip.StatusException(num);
- }
- Image image = CreateImageObject(zero);
- EnsureSave(image, filename, null);
- return image;
- }

- public static Image FromFile(string filename, bool useEmbeddedColorManagement)
- {
- int num;
- if (!File.Exists(filename))
- {
- IntSecurity.DemandReadFileIO(filename);
- throw new FileNotFoundException(filename);
- }
- filename = Path.GetFullPath(filename);
- IntPtr zero = IntPtr.Zero;
- if (useEmbeddedColorManagement)
- {
- num = SafeNativeMethods.Gdip.GdipLoadImageFromFileICM(filename, out zero);
- }
- else
- {
- num = SafeNativeMethods.Gdip.GdipLoadImageFromFile(filename, out zero);
- }
- if (num != 0)
- {
- throw SafeNativeMethods.Gdip.StatusException(num);
- }
- num = SafeNativeMethods.Gdip.GdipImageForceValidation(new HandleRef(null, zero));
- if (num != 0)
- {
- SafeNativeMethods.Gdip.GdipDisposeImage(new HandleRef(null, zero));
- throw SafeNativeMethods.Gdip.StatusException(num);
- }
- Image image = CreateImageObject(zero);
- EnsureSave(image, filename, null);
- return image;
- }

上述源码中多了一些错误处理的代码,但那些代码明显不会太耗时间。这也是我这里用VB6做测试的原因。
结论2:GetThumbnailImage不适合于做快速的图像缩放预览之类的工作,但是却是选择单开单个图像预览时的好选择。
由以上图片测试结果可以看出,GetThumbnailImage是无法胜任任意大小预览模式的,但是对于大哥图像预览时,大部分大小都只有160*120大小的预览窗口的图像,确实非常合适的。
结论3:C#下的Bitmap或者Image类的GetThumbnailImage函数不适合于做预览工作,原因就是他不如我在VB6下工作的快,特别是对于那些已经内嵌了缩略图的图像。如果是用C#做,我可能会像类似于VB中这样,直接调用GDI+的API函数。
测试源码下载: http://files.cnblogs.com/Imageshop/ThumbNail.rar
附在的说一下: JPG的EXIF信息中的缩略图格式其实也是JPG格式,这也可以看成为什么JPG内部不一定非要内嵌缩略图的原因,不然缩略图本身格式也是JPG,那缩略图里有要嵌入缩略图....想想吧,会出现什么。
后记: 用了下美图秀秀,在打开8000*6000这样尺寸的JPG进行预览时,初次打开的速度就很快,没感觉到有延迟,并且图像质量还可以,这个的算法过程期待有高人指点下。
*****************************基本上我不提供源代码,但是我会尽量用文字把对应的算法描述清楚或提供参考文档**************************
*******************************因为靠自己的努力和实践写出来的效果才真正是自己的东西,人一定要靠自己******************************
********************************如果你真的要源代码,那你得把你的腰包变得更瘦一点,这就是偷懒的惩罚*******************************
.net中获取图像缩略图的函数GetThumbnailImage的更多相关文章
- asp.net中生成缩略图并添加版权实例代码
这篇文章介绍了asp.net中生成缩略图并添加版权实例代码,有需要的朋友可以参考一下 复制代码代码如下: //定义image类的对象 Drawing.Image image,newimage; //图 ...
- SQL中Round(),Floor(),Ceiling()函数的浅析
项目中的一个功能模块上用到了标量值函数,函数中又有ceiling()函数的用法,自己找了一些资料,对SQL中这几个函数做一个简单的记录,方便自己学习.有不足之处欢迎拍砖补充 1.round()函数遵循 ...
- avascript中的this与函数讲解
徐某某 一个半路出家的野生程序员 javascript中的this与函数讲解 前言 javascript中没有块级作用域(es6以前),javascript中作用域分为函数作用域和全局作用域.并且,大 ...
- PHP中有关正则表达式的函数集锦
之前学正则表达式的目的是想从网上抓取点小说啊,文档啊,还有获取相应的视频连接然后批量下载.当时初学PHP根本不知道PHP有专门抓包的工具,就像Simple_html_dom.php(在我的其他博文中有 ...
- SQL SERVER中用户定义标量函数(scalar user defined function)的性能问题
用户定义函数(UDF)分类 SQL SERVER中的用户定义函数(User Defined Functions 简称UDF)分为标量函数(Scalar-Valued Function)和表值函数(T ...
- mysql中bit_count和bit_or函数的含义
翻阅mysql手册时,看到有个示例使用了bit_or方法来去除重复的数据,一开始没看明白,后来看明白之后感觉非常巧妙.示例要实现的功能就是计算每月有几天有访问,先把示例摘录在这里. 1 2 3 4 5 ...
- C#中的日期处理函数
C#中的日期处理函数 //2013年4月24日 this.TextBox6.Text = System.DateTime.Now.ToString("D"); //2013-4-2 ...
- 在Excel中使用频率最高的函数的功能和使用方法
在Excel中使用频率最高的函数的功能和使用方法,按字母排序: 1.ABS函数 函数名称:ABS 主要功能:求出相应数字的绝对值. 使用格式:ABS(number) 参数说明:number代表需要求绝 ...
- Loadrunner中web_find和web_reg_find函数的使用与区别
总结一下Loadrunner中的检查点函数,主要介绍两个函数:web_find()和web_reg_find():这两个函数均用于内容的查找,但两者也有本质的区别,具体介绍如下:一.web_find( ...
随机推荐
- The Swift Programming Language-官方教程精译Swift(5)集合类型 -- Collection Types
Swift语言提供经典的数组和字典两种集合类型来存储集合数据.数组用来按顺序存储相同类型的数据.字典虽然无序存储相同类型数据值但是需要由独有的标识符引用和寻址(就是键值对). Swift语言里的数 ...
- 动态创建一些常的html标签
原文:动态创建一些常的html标签 一段时间来,不管是在学习还是应用asp.net mvc应用程序,较多情况之下,需要动态创建一些html标签.如这篇<文本框下面有两个铵钮,点就加点减就减> ...
- 多线程学习之三生产者消费者模式Guarded Suspension
Guarded Suspension[生产消费者模式] 一:guarded suspension的参与者--->guardedObject(被防卫)参与者 1.1该 ...
- 文档PDF开放
108个大数据文档PDF开放下载 投递人 itwriter 发布于 2015-01-29 13:34 评论(13) 有2251人阅读 原文链接 [收藏] « » 文/36 大数据 总有人问我 ...
- 安装后维修指南再次双系统xp、win7、ubuntu
在本文中,主机为单个硬盘,三个步骤. 第一步: 使用liveCD要么u盘引导计算机进入ubuntu.这里最好是确保两个系统版本号之间的一致性. 菜单 System -> Administrati ...
- 注意,WebDeploy服务会占用80端口。(Windows关闭了IIS,80端口任然被占用)
最近遇到一个很奇怪的事情,Windows上的 IIS 网站 全关掉了,80端口仍然被占用.然后我新装了一台服务器,一个一个组件地装,装一个测一次,最后发现,WebDeploy这个组件,会占用80端口. ...
- 安装Windows2003操作系统 - 初学者系列 - 学习者系列文章
Windows 2003是一款经典的服务器操作系统.以前笔者工作的时候就是用的这款操作系统来进行编程的.下面就对该操作系统的安装进行介绍(部分步骤参见XP的安装http://www.cnblogs.c ...
- 等差数列6《MAC》
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXhzdGFycw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA ...
- Maven包装过程中跳过测试
在具体项目,步骤需要跳过假设检验,应采取skipTests此属性设置为true. <pre name="code" class="html">< ...
- Value Object(值对象)如何使用 EF 进行正确映射
DDD 领域驱动设计-Value Object(值对象)如何使用 EF 进行正确映射 写在前面 首先,这篇博文是用博客园新发布的 MarkDown编辑器 编写的,这也是我第一次使用,语法也不是很熟悉, ...