C#中使用FreeImage库加载Bmp、JPG、PNG、PCX、TGA、PSD等25种格式的图像(源码)。
其实我一直都是喜欢自己去做图像格式的解码的(目前我自己解码的图像格式大概有15种),但是写本文主要原因是基于CSDN的这个帖子的:
http://bbs.csdn.net/topics/390510431 用pictureBox显示一个黑白8bit图像,如何消除颗粒感
用于测试的原始的JPG图像: http://files.cnblogs.com/Imageshop/img01.rar
这个帖子中,作者的需要加载一副灰度的8位的PG格式图像,但是利用.net的Bitmap类加载的图像会出现明显颗粒感,由于.net中的Bitmap类是基于GDI+操作的,因此我也是试着用我的Imageshop打开这幅图像(Imageshop内部也是用GDI+的API实现的),同样有颗粒感。因此,我们需要从其他的手段来解决这个问题。
.net下加载的效果 Photoshop打开的效果
首先,我用了VS6.0中的Stdpicture对象来加载这幅图像,能得到正确的结果。然后用PS打开它,也能得到较好的效果,最后用微软的图片查看器,也是可以正确显示的。最后用mspaint(画图)工具打开,则出现了和在.net中一样的效果。
因此,我们的第一理想方案是使用com里的Stdpicture来解决这个问题,其实在VB6.0下,一个LoadPicture函数就可以解决它,但是在C#下要使用它,需要很多API函数来处理,我自己试着搞了下,觉得过于繁琐,因此放弃了。
因此,我把希望投向了比较有名的图像解码的软件FreeImage中,经过试验,发现FreeImage的解码是和PS一致的。
我们先来看看百度对FreeImage的介绍:
由上述可见,FreeImage的侧重点偏向于图像的解码和编码,显示图像则需要用户自己负责,而这正是我们所需要的。
为了能在.NET中使用FreeImage,我知道的有两种方式,一种是直接使用FreeImage 的Flat API,而这需要对使用的API函数进行声明。另外一种方式就是使用FreeImage 提供的FreeImageNET.dll中提供的类库(其实就是对FreeImage.dll中函数的封装)。 我这里把两种方式的实现都简单的描述下:
public static Bitmap LoadImageFormFreeImage(string FileName)
{
Bitmap Bmp = null;
FREE_IMAGE_FORMAT fif = FREE_IMAGE_FORMAT.FIF_UNKNOWN; ;
fif = FreeImage_GetFileType(FileName, );
if (fif == FREE_IMAGE_FORMAT.FIF_UNKNOWN)
{
fif = FreeImage_GetFIFFromFilename(FileName);
} if ((fif != FREE_IMAGE_FORMAT.FIF_UNKNOWN) && (FreeImage_FIFSupportsReading(fif) != ))
{
IntPtr Dib = FreeImage_Load(fif, FileName, );
int Bpp = FreeImage_GetBPP(Dib);
PixelFormat PF;
int Width, Height, Stride;
switch (Bpp)
{
case :
PF = PixelFormat.Format1bppIndexed; break;
case :
PF = PixelFormat.Format4bppIndexed; break;
case :
PF = PixelFormat.Format8bppIndexed; break;
case :
PF = PixelFormat.Format16bppRgb555; break;
case :
PF = PixelFormat.Format24bppRgb; break;
case :
PF = PixelFormat.Format32bppArgb; break;
default:
FreeImage_Free(Dib);
return null;
}
Width = FreeImage_GetWidth(Dib); // 图像宽度
Height = FreeImage_GetHeight(Dib); // 图像高度
Stride = FreeImage_GetPitch(Dib); // 图像扫描行的大小,必然是4的整数倍 /** 方案1:存在内存泄露
* FreeImage_FlipVertical(Dib); // 因为FreeImage的认为的图像的起点在左下角,不进行翻转则图像的倒过来的
* IntPtr Bits = FreeImage_GetBits(Dib); // 得到图像数据在内存中的地址
* Bmp = new Bitmap(Width, Height, Stride, PF, Bits); // 实际上调用的GdipCreateBitmapFromScan0函数从内存创建位图
**/ //方案2:
IntPtr Bits = FreeImage_GetBits(Dib);
Bmp = new Bitmap(Width, Height, Stride, PF, Bits);
Bmp.RotateFlip(RotateFlipType.RotateNoneFlipY); // 调用GDI+自己的旋转函数
if (Bpp <= )
{
ColorPalette Pal = Bmp.Palette; // 设置调色板
RGBQUAD* GdiPal = FreeImage_GetPalette(Dib);
int ClrUsed = FreeImage_GetColorsUsed(Dib);
for (int I = ; I < ClrUsed; I++)
{
Pal.Entries[I] = Color.FromArgb(, GdiPal[I].Red, GdiPal[I].Green, GdiPal[I].Blue);
}
Bmp.Palette = Pal;
}
FreeImage_Free(Dib); // 使用方案2则可以立马释放
return Bmp;
}
return null;
}
}
上述代码中,我们对方案1为什么存在内存泄露做一定的说明。
方案1中,Bmp = new Bitmap(Width, Height, Stride, PF, Bits)这条语句实际上调用了GDI+的函数GdipCreateBitmapFromScan0从内存创建位图,通过此种方式创建的位图并没有新分配一块内存给创建的位图,而是和Bits对应的内存绑定的。您可以用如下的代码验证这一点:
BitmapData BmpData = Bmp.LockBits(new Rectangle(0, 0, Bmp.Width, Bmp.Height), ImageLockMode.ReadWrite, Bmp.PixelFormat);
if (BmpData.Scan0 == Bits )
MessageBox.Show ("通过GDI+创建的图像和FreeImage的DIB对象公用同一内存.")
Bmp.UnlockBits(BmpData);
正是由于这个原因的存在,如果采用方案1,我们不能在创建GDI+的位图后立马释放FreeImage的创建的DIB对象,即不能调用FreeImage_Free(Dib);如果调用了,对应的 Bmp对象实际上是个空对象了。
这样的话也许可能没有关系,我们只要在适当的地方调用Bmp.Dispose,不就可以了吗,你可以做个试验,使用这段代码,然后不断的打开新图像,你会发现程序占用的内存会不断的增加,而没有释放。因此,我们看看MSDN对GdipCreateBitmapFromScan0这个函数是怎么解释的,特别是最后一个参数:
- scan0 [in]
-
Type: BYTE*
Pointer to an array of bytes that contains the pixel data. The caller is responsible for allocating and freeing the block of memory pointed to by this parameter.
上述文字表示用户需要对分配的内存进行释放,也就是说Dispose方法无法释放该部分内存。
有了上述的问题,我们转而使用方案2,方案2使用了一句Bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);这个语句会创建一副新的位图,也就是说进行旋转后的图像已经不再同FreeImage使用同一块内存了。那么此时就可以放心的释放掉FreeImage的DIB对象了。
本以为RotateFlip函数会降低效率,测试表面微软对这个函数的执行效率还是很高的,其实这个函数的函数完全可以借助于CopyMemory函数来高速实现。
当图像的位深小于8时,需要获取调色板的数据。但是我对认为上述获取调色板的FreeImage_GetPalette函数存在内存泄露,无法释放这些RGBQUAD*分配的内存的。FreeImage应该考虑使用类似于GDI+中获取调色板数据那种方式。
使用FreeImageNET.dll中提供的类库,则编写代码更为方便,推荐使用第二种方式,朋友们可以参考附件。
实际上FreeImage还有很多强大的功能,比如色深转换、充分利用它洗看图软件,格式批处理那是很快捷方便的。
附件中的拖动图像的方式我认为也是值得作为大家学习的。
http://files.cnblogs.com/Imageshop/FreeImage.rar
http://files.cnblogs.com/Imageshop/FreeImageApi.rar
***************************作者: laviewpbt 时间: 2013.7.7 联系QQ: 33184777 转载请保留本行信息*************************
C#中使用FreeImage库加载Bmp、JPG、PNG、PCX、TGA、PSD等25种格式的图像(源码)。的更多相关文章
- delphi 中 image 控件加载bmp、JPG、GIF、PNG等图片的办法
procedure TForm1.Button1Click(Sender: TObject);var jpg: TJPEGImage; // 要use Jpeg单元begin // 显示jpg大图 ...
- [转]Delphi 中 image 控件加载bmp、JPG、GIF、PNG等图片的办法
procedure TForm1.Button1Click(Sender: TObject); var jpg: TJPEGImage; // 要use Jpeg单元 begin // 显示jpg大图 ...
- EF如何操作内存中的数据以及加载相关联表的数据:延迟加载、贪婪加载、显示加载
之前的EF Code First系列讲了那么多如何配置实体和数据库表的关系,显然配置只是辅助,使用EF操作数据库才是每天开发中都需要用的,这个系列讲讲如何使用EF操作数据库.老版本的EF主要是通过Ob ...
- imagesLoaded – 检测网页中的图片是否加载
imagesLoaded 是一个用于来检测网页中的图片是否载入完成的 JavaScript 工具库.支持回调的获取图片加载的进度,还可以绑定自定义事件.可以结合 jQuery.RequireJS 使用 ...
- LIB库加载方法-引用百度百科
LIB库加载方法,有三种,如下: 1.LIB文件直接加入到工程文件列表中 在VC中打开File View一页,选中工程名,单击鼠标右键,然后选中\"Add Files to Project\ ...
- Android OpenGL库加载过程源码分析
Android系统采用OpenGL绘制3D图形,使用skia来绘制二维图形:OpenGL源码位于: frameworks/native/opengl frameworks/base/opengl 本文 ...
- Qt中如何 编写插件 加载插件 卸载插件
Qt中如何 编写插件 加载插件 卸载插件是本文要介绍的内容.Qt提供了一个类QPluginLoader来加载静态库和动态库,在Qt中,Qt把动态库和静态库都看成是一个插件,使用QPluginLoade ...
- linux动态库加载RPATH, RUNPATH
摘自http://gotowqj.iteye.com/blog/1926771 linux动态库加载RPATH, RUNPATH 链接动态库 如何程序在连接时使用了共享库,就必须在运行的时候能够找到共 ...
- 018 关联映射文件中<class>标签中的lazy(懒加载)属性
Lazy(懒加载): 只有在正真使用该对象时,才会创建这个对象 Hibernate中的lazy(懒加载): 只有我们在正真使用时,它才会发出SQL语句,给我们去查询,如果不使用对象则不会发SQL语句进 ...
随机推荐
- ISS部署网站--HTTP 错误 404.17 - Not Found 请求的内容似乎是脚本,因而将无法由静态文件处理程序来处理。
1.控制面板>程序和功能>打开或关闭Windows功能 > Internet信息服务 > 万维网服务 > 应用程序开发功能 > ASP.NET(看这个是否选上): ...
- 什么是Servlet?
HTML只能用来保存静态内容,而通常情况下,静态页面很难满足实际应用的需要,鉴于此,动态页面被引入.所谓动态页面,指的是能够根据不同时间,不同用户而显示不同内容的页面,例如常见的论坛.留言板.电子商务 ...
- GJM : 各大开发游戏引擎
感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...
- html5手机端遮罩弹出菜单代码
效果体验:http://hovertree.com/texiao/html5/17/ 效果图: 代码如下: <!doctype html> <html lang="zh&q ...
- 关于图片的PNG与JPG、JIF格式
一:GIF(Graphics Interchange Format) 简介 GIF图形交换格式是一种位图图形文件格式,以8位色(即256种颜色)重现真彩色的图像. 它实际上是一种压缩文档,采用LZW压 ...
- JQ中的方法、事件及动画
css( ) 除了可以为元素添加样式外,还可用来查询元素,某样式值alert($('.cls1').css('width')); //100px(返回带单位的值)注意:原生CSS样式中有-的去掉并且将 ...
- 学习笔记 MYSQL盲注
猜解当前数据库名 输入 ' # ,显示不存在: 输入 ' # ,显示不存在: 输入 ' # ,显示不存在: 输入 ' # ,显示存在: 采用二分法猜解数据库名 输入 ' ,)) # ,显示存在,说明数 ...
- 修改ArcSDE的最大连接数
我们大体都知道ArcSDE的连接数有 48 的限制,很多人也知道这个参数可以修改,并且每种操作系统能支持的最大连接数是不同的. 如果应用报错:超出系统最大连接数该如何处理? 两种解决办法: 第一,首先 ...
- 常用ArcGIS for Silverlight 开发API介绍
1.API介绍 2.Map对象 3.Layer对象 4.Symbol对象 5.Task对象
- Servlet、Filter、Listener、Interceptor
首先,JSP/Servlet规范中定义了Servlet.Filter.Listener这三种角色,并没有定义Interceptor这个角 色,Interceptor是某些MVC框架中的角色,比如Str ...