原文链接: http://blog.csdn.net/chenlycly/article/details/24112955

  与GDI相比,GDI+要强大很多。对于Windows应用程序来说,用GDI是比较多的,也是比较熟练的,GDI+相对用的较少一点,但是现在GDI+的使用已经很普遍了。GDI+支持各种类型图片的处理,比如常见的bmp、jpg、gif、png等类型,特别是GDI+处理png图片时有很大的优势。有时我们需要将图片文件加载到内存中,然后进行UI的绘制,由于要支持多种类型的图片的载入,所以首先想到的是使用GDI+中的图片处理类Image或Bitmap。有时我们也需要将内存中的位图数据,保存成各种类型的图片文件,我们也要用到图片处理类Image或Bitmap。GDI+功能强大,但相对GDI而言,要难用很多,在使用的过程中也有很多需要注意的地方。下面结合本人在实际开发过程中遇到的问题,进行一些总结,以供参考。

        1、GDI+库的加载与卸载

在程序初始化时,添加加载GDI+的代码:

  1. ULONG_PTR m_gdiplusToken;
  2. // 初始化GDI+
  3. Gdiplus::GdiplusStartupInput gdiplusStartupInput;
  4. Gdiplus::GdiplusStartup( &m_gdiplusToken, &gdiplusStartupInput, NULL );

在程序退出时,添加卸载GDI+的代码:

  1. // 释放GDI+资源
  2. Gdiplus::GdiplusShutdown( m_gdiplusToken );

在使用GDI+中相关函数和结构时,尽量加上Gdiplus命名空间名,以防止与其他模块的代码因为字段的名称相同出现冲突。比如,GDI+库中定义GDI+函数执行结果的每句类型Status,定义如下所示。如果我们需要判断函数是否正确执行,应该将返回值和Gdiplus::Ok,而不是直接和Ok比较,注意这个加上Gdiplus命名空间名的好习惯。

  1. enum Status
  2. {
  3. Ok = 0,
  4. GenericError = 1,
  5. InvalidParameter = 2,
  6. OutOfMemory = 3,
  7. ObjectBusy = 4,
  8. InsufficientBuffer = 5,
  9. NotImplemented = 6,
  10. Win32Error = 7,
  11. WrongState = 8,
  12. Aborted = 9,
  13. FileNotFound = 10,
  14. ValueOverflow = 11,
  15. AccessDenied = 12,
  16. UnknownImageFormat = 13,
  17. FontFamilyNotFound = 14,
  18. FontStyleNotFound = 15,
  19. NotTrueTypeFont = 16,
  20. UnsupportedGdiplusVersion = 17,
  21. GdiplusNotInitialized = 18,
  22. PropertyNotFound = 19,
  23. PropertyNotSupported = 20,
  24. #if (GDIPVER >= 0x0110)
  25. ProfileNotFound = 21,
  26. #endif //(GDIPVER >= 0x0110)
  27. };

      2、静态函数FromFile、FromHBitmap和FromStream的使用

FromFile主要是将图片文件加载到GDI+对象中,FromHBitmap和FromStream函数则是将内存中的图片数据加载到GDI+对象中。我们平常处理图片加载与格式转换时主要用到两个类:Bitmap类和Image类。Bitmap类继承于Image类,这三个函数它都有。Image类则只有FromFile和FromStream函数。在使用这三个函数时,要注意一下几点。

(1)  对于FromFile、FromHBitmap和FromStream这三个函数,都是静态函数,MSDN对于返回值的说明:This method returns a pointer to the new Bitmap/Image object(在VS中GO到函数的定义出也是能看出来的,函数返回是new出来的对象)。这意味着什么呢?因为返回的是新创建的类的对象,是需要我们使用者来负责销毁的,即对象使用完了后需要我们手动将之delete掉。如果不delete掉,不仅会导致内存泄漏,也会导致GDI句柄泄漏。这点在我们的项目开发中是深有体会的,特别是GDI句柄泄漏使用了专门的工具进行检测的。

(2) 在使用Image::FromFile时,要注意将指定的文件加载到Image对象中后,会将磁盘上对应的文件“锁住”,其他地方如果要同时加载该文件则可能会出问题,这也是我们在开发过程中遇到的问题。我们的处理办法是,不使用Image::FromFile函数,使用Image::FromStream。对于Image::FromStream,我们先将文件读到内存中,然后再将内存中数据倒到流中,然后调用Image::FromStream从流中将图片数据加载到Image对象中。使用Image::FromStream的流程较复杂,使用时要注意,也有一些陷阱,下面我们会谈到。

(3) 对于GDI+提供的函数,对于需要传入字符串的参数,一般均是WCHAR*宽字节类型,所以在调用之前要确保传入字符串是宽字节的。这点和COM接口类似,一般都要传入宽字节的字符串。

    3、Image::FromStream的使用

此处主要讲如何将图片文件加载到Image对象中的,使用Image::FromStream加载的流程大概为:先将图片文件读到HGLOBAL内存中,然后调用CreateStreamOnHGlobal函数在HGLOBAL内存数据基础上创建流,最后调用Image::FromStream将图片数据加载到new出来的Image对象中。相关的代码如下所示:

  1. Image* m_pImg; // 定义成CXXXXXXXXX类的成员变量
  2. BOOL CXXXXXXXXX::Load( LPCTSTR pszFileName )
  3. {
  4. ASSERT( pszFileName != NULL );
  5. CFile file;
  6. DWORD dwSize;
  7. // 打开文件
  8. if ( !file.Open( szFileName,
  9. CFile::modeRead |
  10. CFile::shareDenyWrite ) )
  11. {
  12. TRACE( _T( "Load (file): Error opening file %s\n" ), szFileName );
  13. return FALSE;
  14. };
  15. // 根据文件大小分配HGLOBAL内存
  16. dwSize = (DWORD)file.GetLength();
  17. HGLOBAL hGlobal = GlobalAlloc( GMEM_MOVEABLE | GMEM_NODISCARD, dwSize );
  18. if ( !hGlobal )
  19. {
  20. TRACE( _T( "Load (file): Error allocating memory\n" ) );
  21. return FALSE;
  22. };
  23. char *pData = reinterpret_cast<char*>(GlobalLock(hGlobal));
  24. if ( !pData )
  25. {
  26. TRACE( _T( "Load (file): Error locking memory\n" ) );
  27. GlobalFree( hGlobal );
  28. return FALSE;
  29. };
  30. // 将文件内容读到HGLOBAL内存中
  31. TRY
  32. {
  33. file.Read( pData, dwSize );
  34. }
  35. CATCH( CFileException, e );
  36. {
  37. TRACE( _T( "Load (file): An exception occured while reading the file %s\n"),
  38. szFileName );
  39. GlobalFree( hGlobal );
  40. e->Delete();
  41. file.Close();
  42. return FALSE;
  43. }
  44. END_CATCH
  45. GlobalUnlock( hGlobal );
  46. file.Close();
  47. // 利用hGlobal内存中的数据创建stream
  48. IStream *pStream = NULL;
  49. if ( CreateStreamOnHGlobal( hGlobal, TRUE, &pStream ) != S_OK )
  50. {
  51. return FALSE;
  52. }
  53. m_pImg = Image::FromStream( pStream );
  54. ASSERT( m_pImg != NULL )
  55. // 要加上这一句,否则由GlobalAlloc得来的hGlobal内存没有被释放,导致内存泄露,由于
  56. // CreateStreamOnHGlobal第二个参数被设置为TRUE,所以调用pStream->Release()会自动
  57. // 将hGlobal内存(参见msdn对CreateStreamOnHGlobal的说明)
  58. pStream->Release();
  59. .......// 后续代码此处省略
  60. }

如上面的代码,必须要加上pStream->Release();这句,否则会导致内存泄漏,因为上面GlobalAlloc来的内存没有释放。但是代码中使用完后并没有调用GlobalFree来释放内存,那自动释放内存是如何做到的呢?那我们就来看看MSDN中,对CreateStreamOnHGlobal函数的说明:

  1. WINOLEAPI CreateStreamOnHGlobal(
  2. __in          HGLOBAL hGlobal,
  3. __in          BOOL fDeleteOnRelease,   // 主要看这个参数的说明
  4. __out         LPSTREAM* ppstm
  5. );

参数fDeleteOnRelease的说明:A value that indicates whether the underlying handle for this stream object should be automatically freed when the stream object is released.If set to FALSE, the caller must free the hGlobal after the final release. If set to TRUE, the final release will automatically free the hGlobal parameter.

也就是说,当将fDeleteOnRelease参数设置为FALSE时,调用pStream->Release();时就不会自动释放GlobalAlloc来的内存,此时必须手动调用GlobalFree来释放;当将fDeleteOnRelease参数设置为TRUE时,在调用pStream->Release();是会自动将GlobalAlloc来的内存释放掉。

       4、GDI+的绘图渲染能力

当我们在用GDI绘制斜线线条(非水平线条、非竖直线条)时,会有明显的锯齿,看起来效果不太好。用GDI+绘制则要好很多,因为GDI+的渲染效果要比GDI好很多,平滑很多。

使用GDI+进行图片处理时要注意的问题的更多相关文章

  1. 使用 jQuery 操作页面元素的方法,实现浏览大图片的效果,在页面上插入一幅小图片,当鼠标悬停到小图片上时,在小图片的右侧出现与之相对应的大图片

    查看本章节 查看作业目录 需求说明: 使用 jQuery 操作页面元素的方法,实现浏览大图片的效果,在页面上插入一幅小图片,当鼠标悬停到小图片上时,在小图片的右侧出现与之相对应的大图片 实现思路: 在 ...

  2. 【VC++技术杂谈007】使用GDI+进行图片格式转换

    本文主要介绍如何使用GDI+对图片进行格式转换,可以转换的图片格式为bmp.jpg.png. 1.加载GDI+库 GDI+是GDI图形库的一个增强版本,提供了一系列Visual C++ API.为了使 ...

  3. C++使用GDI+实现图片格式转换

    主要是我在设置壁纸时遇到的个小问题,因为设置壁纸只能是bmp格式的图片,不可能我喜欢的壁纸就都是bmp格式的,就想怎么转换一下图片的格式,于是就在百度搜怎么弄,搜到了可行方法,却没有实现代码,有些看起 ...

  4. IE8下jQuery改变png图片透明度时出现的黑边问题

    png24格式的图片在用jQuery添加显示隐藏动画时发现,图片的半透明区域出现黑边? 在网上搜了搜主要有以下几种办法: 1.把图片保存成PNG-8格式. 2.把背景色一起切入并保存为JPG格式. 以 ...

  5. 图片缩放时java.lang.IllegalArgumentException: pointerIndex out of range解决方案

    版权声明:本文为博主原创文章,未经博主允许不得转载. 06-03 20:45:24.143: E/AndroidRuntime(1230): FATAL EXCEPTION: main06-03 20 ...

  6. 一种使用GDI+对图片尺寸和质量的压缩方法

    今天同事向我询问图片压缩的算法.我想起大概两三年前做过的一个项目. 当中包括了尺寸和质量两种压缩算法.而且支持JPEG.bmp.PNG等格式. 今天把这段逻辑贴出来,供大家參考.(转载请指明来源于br ...

  7. php 图片合成时文字颜色丢失

    最近在做图片合成的时候无意间发现文字颜色丢失了,仔细找了以后才发现原来是因为图片格式的原因 当图片是png图片时文字的颜色就变成了白色的,So.........去你妹的png,用jpg吧! $dest ...

  8. C# 使用 GDI+ 给图片添加文字,并使文字自适应矩形区域

    需求 需求是要做一个编辑文字的页面.用户在网页端写文字,文字区域是个矩形框,用户可以通过下方的拖动条调节文字大小. 如下图: 提交数据的时候前端传文字区域的左上角和右下角定位给后台.因为前端的字体大小 ...

  9. C# Winform控件对透明图片重叠时导致图片不透明的解决方法(转)

    在Winform中如果将一个透明图片放在窗体上能正常显示透明,但是如果将该图片放在另一个控件上会导致不能显示透明效果. 解决这种情况,可以采取在控件上使用GDI+绘画出透明图片. 这里我们就以一个pi ...

随机推荐

  1. oracle 复制表结构表数据

    create table Uc_t_Department3 as (select * from Uc_t_Department where 1=2);insert into Uc_t_Departme ...

  2. 磁盘I/O的性能评估方法

    磁盘I/O的性能评估方法 http://blog.synology.com/blog/?p=2086 通常,我们很容易观察到数据库服务器的内存和CPU压力.但是对I/O压力没有直观的判断方法.磁盘有两 ...

  3. AngularJS实现三级Table列表

    angular.module('yo03App') .controller('MyrouteCtrl', function ($scope) { $scope.professors = [{ 'nam ...

  4. 安装 mysql-5.6.4-m7

    1.首先下载:http://cdn1.mydown.yesky.com/soft/201403/mysql-5.6.4-m7.tar.gz 2.解压,进入目录,执行cmake ./(如果没有cmake ...

  5. 修改浏览器Cookie

    Firefox:安装 Web Developer 插件,装完重启之后会出现一排新的工具栏,点击 Cookies -> Add Cookie… 即可增加/修改一个Cookie了. Opera:Op ...

  6. windows能连网络,可是打不开网页,出现无法解析server的DNS

    首先,我想先说作为一名刚学完计算机网络课的学生,并且还完毕了学校的号称和斯坦福一个难度级别的网络实验之后.我觉得自己对于计算机网络还是勉强入门了.对于各种之前仅仅是听过的各种tcp.http.路由器. ...

  7. php反射API 获取属性/注释/方法 执行方法 实例

    <?php class Person { /** * 这里是对$_allowDynamicAttributes的注释信息 */ private $_allowDynamicAttributes ...

  8. Centos6下编译LEDE/OpenWrt

    准备工作 1. 安装依赖软件 这是官方文档提供的依赖列表 yum install subversion binutils bzip2 gcc gcc-c++ gawk gettext flex ncu ...

  9. 给Java程序员的几条建议

    对于Java程序猿学习的建议 这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是LZ你是如何学习Java的,能不能给点建议? 今天LZ是打算来点干货,因此咱们就不说一些学 ...

  10. selectAll, unSelectAll两个操作的实现

    private void updateBatchSelectionStatus() {     ContactListAdapter.ViewHolder viewHolder = null;     ...