使用GDI+进行图片处理时要注意的问题

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

       
1、GDI+库的载入与卸载

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

      ULONG_PTR m_gdiplusToken;

// 初始化GDI+
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup( &m_gdiplusToken, &gdiplusStartupInput, NULL );



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

       // 释放GDI+资源
Gdiplus::GdiplusShutdown( m_gdiplusToken );
         
在使用GDI+中相关函数和结构时,尽量加上Gdiplus命名空间名,以防止与其它模块的代码由于字段的名称同样出现冲突。比方,GDI+库中定义GDI+函数运行结果的每句类型Status,定义例如以下所看到的。假设我们须要推断函数是否正确运行,应该将返回值和Gdiplus::Ok,而不是直接和Ok比較,注意这个加上Gdiplus命名空间名的好习惯。

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

     
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对象中。相关的代码例如以下所看到的:

Image* m_pImg; // 定义成CXXXXXXXXX类的成员变量

BOOL CXXXXXXXXX::Load( LPCTSTR pszFileName )
{
ASSERT( pszFileName != NULL );

CFile file;
DWORD dwSize;

// 打开文件
if ( !file.Open( szFileName,
CFile::modeRead |
CFile::shareDenyWrite ) )
{
TRACE( _T( "Load (file): Error opening file %s\n" ), szFileName );
return FALSE;
};

// 依据文件大小分配HGLOBAL内存
dwSize = (DWORD)file.GetLength();
HGLOBAL hGlobal = GlobalAlloc( GMEM_MOVEABLE | GMEM_NODISCARD, dwSize );
if ( !hGlobal )
{
TRACE( _T( "Load (file): Error allocating memory\n" ) );
return FALSE;
};

char *pData = reinterpret_cast<char*>(GlobalLock(hGlobal));
if ( !pData )
{
TRACE( _T( "Load (file): Error locking memory\n" ) );
GlobalFree( hGlobal );
return FALSE;
};

// 将文件内容读到HGLOBAL内存中
TRY
{
file.Read( pData, dwSize );
}
CATCH( CFileException, e );
{
TRACE( _T( "Load (file): An exception occured while reading the file %s\n"),
szFileName );
GlobalFree( hGlobal );
e->Delete();
file.Close();
return FALSE;
}
END_CATCH

GlobalUnlock( hGlobal );
file.Close();

// 利用hGlobal内存中的数据创建stream
IStream *pStream = NULL;
if ( CreateStreamOnHGlobal( hGlobal, TRUE, &pStream ) != S_OK )
{
return FALSE;
}

m_pImg = Image::FromStream( pStream );
ASSERT( m_pImg != NULL )

// 要加上这一句,否则由GlobalAlloc得来的hGlobal内存没有被释放,导致内存泄露,由于
// CreateStreamOnHGlobal第二个參数被设置为TRUE,所以调用pStream->Release()会自己主动
// 将hGlobal内存(參见msdn对CreateStreamOnHGlobal的说明)
pStream->Release();

.......// 兴许代码此处省略
}


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

WINOLEAPI CreateStreamOnHGlobal(
__in HGLOBAL hGlobal,
__in BOOL fDeleteOnRelease, // 主要看这个參数的说明
__out LPSTREAM* ppstm
);
參数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+进行图片处理时要注意的问题,布布扣,bubuko.com

时间: 2024-12-19 01:14:37

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

鼠标移动到图片上时,显示大图片

HTML标签中的一部分,仅供参考 <tr> <td><input type="checkbox" class="checkbox1"/></td> <td>1002</td> <td>小猫咪</td> <td><img src="img/02.jpg" height="100" width="100&qu

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

今天同事向我询问图片压缩的算法,我想起大概两三年前做过的一个项目.其中包含了尺寸和质量两种压缩算法,并且支持JPEG.bmp.PNG等格式.今天把这段逻辑贴出来,供大家参考.(转载请指明来源于breaksoftware的CSDN博客) 尺寸压缩 bool CompressImagePixel( const WCHAR* pszOriFilePath, const WCHAR* pszDestFilePah, UINT ulNewHeigth, UINT ulNewWidth ) { // Ini

CSS如何实现当鼠标放在图片上时改变边框

CSS如何实现当鼠标放在图片上时改变边框:建议:尽可能的手写代码,可以有效的提高学习效率和深度.很多网页都有这样的效果,当鼠标放在图片链接上的时候,图片的边框会发生变化.下面就简单介绍一下如何实现此种效果.实例代码如下: <!DOCTYPE html> <html> <head> <meta charset=" utf-8"> <meta name="author" content="http://ww

图片缩放时java.lang.IllegalArgumentException: pointerIndex out of range

06-03 20:45:24.143: E/AndroidRuntime(1230): FATAL EXCEPTION: main 06-03 20:45:24.143: E/AndroidRuntime(1230): java.lang.IllegalArgumentException: pointerIndex out of range 06-03 20:45:24.143: E/AndroidRuntime(1230): at android.view.MotionEvent.native

使用Net.Sockets.TcpListener和Net.Sockets.TcpClient进行图片传输时如何精确控制接收缓存数组大小

<span style="font-size:18px;">在dotnet平台Net.Sockets.TcpListener和Net.Sockets.TcpClient已经为我们封装了所有Socket关于tcp部分,操作也更为简单,面向数据流.使用TcpClient的GetStream方法获取数据流后可以方便的对数据流进行读写操作,就如同本地磁盘的文件读写一样,使得程序员在设计程序时更为便捷简单.</span> 但如果你使用过这两个对象进行数据传输的时候,你会发

DIV CSS鼠标经过悬停在图片上时图片上方显示文字(转)

DIV CSS鼠标悬停在没有文字内容图片上时图片上方显示文字,完全是纯css div实现.CSS实现鼠标悬停放图片上方时显示美化内容. 原始图片显示没有文字在上方,当鼠标经过悬停时显示文字并且文字背景为半透明.可以使用纯DIV+CSS实现鼠标悬停图片上显示文字内容. 纯div+css实现第一张图片没有文字内容,鼠标悬停时出现第二张图那样效果 使用div css实现鼠标悬停图片上方时显示文字内容原理: 首先我们设置一个盒子对象,并且将图片使用style标签内设置为CSS背景图片,同时设置该对象ht

使用Intent启动图片裁剪时遇到的问题:Intent#setData()与Intent#setType()赋值问题

在做使用Intent启动图片裁剪时遇到一个问题: 分开调用Intent#setData();Intent#setType();这两个方法时,程序无法正确运行,但使用Intent#setDataAndType();程序正常运行. 原因分析: 查看Intent#setData(),Intent#setType()源码: public Intent setData(Uri data) { mData = data; mType = null; return this; } public Intent

图片裁切时遇到的问题。

CMYK:四色印刷,电脑显示器是RGB, CMYK格式在图片裁切时,需要手动将CMYK转换为RGB格式,才行. 转换类库: https://jai-imageio.dev.java.net/

纵向文字滚动代码,带上下图片控制的。鼠标放到上下图片上时滚动

<style type="text/css"> #swsh .swsh_body{height:352px;overflow:hidden;}</style></p> <div id="swsh"> <script language="javascript"> var UDMoveflag=true function scrollStart(object,offset){ object.