在很多资料上都说CImage类是基于GDI+的,可是为什么是基于GDI+的呢?
由于使用这个类时,并没有增加#include <gdiplus.h> 。也没有在程序開始和结束时分别写GDI+启动代码GdiplusStartupInput和结束代码GdiplusShutdown
使用这个类时。只须要加入头文件# include<altimage.h>就能够了,比GDI+得使用要简单一些。
而CImage 对图片的处理非常类似GDI+ 。其内部是不是封装了GDI+呢? 幸好,CImage类 是源代码公开的,我们能够研究其源代码。以便加深理解。
首先,看看altimage.h头文件
plaincopy
- #ifndef __ATLIMAGE_H__
- #define __ATLIMAGE_H__
- #pragma once
- #include <atldef.h>
- #include <atlbase.h>
- #include <atlstr.h>
- #include <atlsimpcoll.h>
- #include <atltypes.h>
- #ifndef _ATL_NO_PRAGMA_WARNINGS
- #pragma warning (push)
- #pragma warning(disable : 4820) // padding added after member
- #endif //!_ATL_NO_PRAGMA_WARNINGS
- #pragma warning( push, 3 )
- #pragma push_macro("new")
- #undef new
- #include <gdiplus.h> // 注意这里:加入了GDI+得头文件
- #pragma pop_macro("new")
- #pragma warning( pop )
- #include <shlwapi.h>
- #ifndef _ATL_NO_DEFAULT_LIBS
- #pragma comment(lib, "gdi32.lib")
- #pragma comment(lib, "shlwapi.lib")
- #pragma comment(lib, "gdiplus.lib")
- #if WINVER >= 0x0500
- #pragma comment(lib, "msimg32.lib")
- #endif // WINVER >= 0x0500
- #endif // !_ATL_NO_DEFAULT_LIBS
- #pragma pack(push, _ATL_PACKING)
上面包括了GDI+得头文件
再来看CImage的定义:
plaincopy
- class CImage
- {
- private:
- class CDCCache
- {
- public:
- CDCCache() throw();
- ~CDCCache() throw();
- HDC GetDC() throw();
- void ReleaseDC( HDC ) throw();
- private:
- HDC m_ahDCs[CIMAGE_DC_CACHE_SIZE];
- };
- class CInitGDIPlus
- {
- public:
- CInitGDIPlus() throw();
- ~CInitGDIPlus() throw();
- bool Init() throw();
- void ReleaseGDIPlus() throw();
- void IncreaseCImageCount() throw();
- void DecreaseCImageCount() throw();
- private:
- ULONG_PTR m_dwToken;
- CRITICAL_SECTION m_sect;
- LONG m_nCImageObjects;
- };
plaincopy
- static CInitGDIPlus s_initGDIPlus;
plaincopy
- static CDCCache s_cache;
它定义了两个类成员变量: 当中CInitGDIPlus 是负责GDI+的启动和释放
我们再看一下,这个成员类的Init()方法:
plaincopy
- inline bool CImage::CInitGDIPlus::Init() throw()
- {
- EnterCriticalSection(&m_sect);
- bool fRet = true;
- if( m_dwToken == 0 )
- {
- Gdiplus::GdiplusStartupInput input;
- Gdiplus::GdiplusStartupOutput output;
- Gdiplus::Status status = Gdiplus::GdiplusStartup( &m_dwToken, &input, &output ); //启动GDI+
- if( status != Gdiplus::Ok )
- fRet = false;
- }
- LeaveCriticalSection(&m_sect);
- return fRet;
- }
也就是说 使用这个函数 启动GDI+
再看下一个函数:
plaincopy
- inline void CImage::CInitGDIPlus::ReleaseGDIPlus() throw()
- {
- EnterCriticalSection(&m_sect);
- if( m_dwToken != 0 )
- {
- Gdiplus::GdiplusShutdown( m_dwToken );
- }
- m_dwToken = 0;
- LeaveCriticalSection(&m_sect);
- }
也就是说, 使用这一个函数,用来关闭GDI+
到此,我们便可知道。CImage类是基于GDI+的,可是我们还不知道CImage 对象是不是在初始化时就启动了GDI+?假设不是。那什么时候才启动GDI+呢?
为解决这个疑惑,我们查看CImage 构造函数
plaincopy
- inline CImage::CImage() throw() :
- m_hBitmap( NULL ),
- m_pBits( NULL ),
- m_hDC( NULL ),
- m_nDCRefCount( 0 ),
- m_hOldBitmap( NULL ),
- m_nWidth( 0 ),
- m_nHeight( 0 ),
- m_nPitch( 0 ),
- m_nBPP( 0 ),
- m_iTransparentColor( -1 ),
- m_bHasAlphaChannel( false ),
- m_bIsDIBSection( false )
- {
- s_initGDIPlus.IncreaseCImageCount();
- }
plaincopy
- inline void CImage::CInitGDIPlus::IncreaseCImageCount() throw()
- {
- EnterCriticalSection(&m_sect);
- m_nCImageObjects++;
- LeaveCriticalSection(&m_sect);
- }
由此可见,构造函数并没有启动GDI+
也就是说定义 CImage image; 这个image变量时。并没有启动GDI+
我们继续查找:
plaincopy
- inline bool CImage::InitGDIPlus() throw()
- {
- bool bSuccess = s_initGDIPlus.Init();
- return( bSuccess );
- }
CImage使用InitGDIPlus() 来初始化GDI+
因此我们查找InitGDIPlus() 的全部引用 。发现下面函数:
plaincopy
- inline HRESULT CImage::GetImporterFilterString( CSimpleString& strImporters,
- CSimpleArray< GUID >& aguidFileTypes, LPCTSTR pszAllFilesDescription /* = NULL */,
- DWORD dwExclude /* = excludeDefaultLoad */, TCHAR chSeparator /* = '|' */ )
- {
- if( !InitGDIPlus() )
- {
- return( E_FAIL );
- }
- UINT nCodecs;
- UINT nSize;
- Gdiplus::Status status;
- Gdiplus::ImageCodecInfo* pCodecs;
- status = Gdiplus::GetImageDecodersSize( &nCodecs, &nSize );
- USES_ATL_SAFE_ALLOCA;
- pCodecs = static_cast< Gdiplus::ImageCodecInfo* >( _ATL_SAFE_ALLOCA(nSize, _ATL_SAFE_ALLOCA_DEF_THRESHOLD) );
- if( pCodecs == NULL )
- return E_OUTOFMEMORY;
- status = Gdiplus::GetImageDecoders( nCodecs, nSize, pCodecs );
- BuildCodecFilterString( pCodecs, nCodecs, strImporters, aguidFileTypes, pszAllFilesDescription, dwExclude, chSeparator );
- return( S_OK );
- }
- inline HRESULT CImage::GetExporterFilterString( CSimpleString& strExporters,
- CSimpleArray< GUID >& aguidFileTypes, LPCTSTR pszAllFilesDescription /* = NULL */,
- DWORD dwExclude /* = excludeDefaultSave */, TCHAR chSeparator /* = '|' */ )
- {
- if( !InitGDIPlus() )
- {
- return( E_FAIL );
- }
- UINT nCodecs;
- UINT nSize;
- Gdiplus::Status status;
- Gdiplus::ImageCodecInfo* pCodecs;
- status = Gdiplus::GetImageDecodersSize( &nCodecs, &nSize );
- USES_ATL_SAFE_ALLOCA;
- pCodecs = static_cast< Gdiplus::ImageCodecInfo* >( _ATL_SAFE_ALLOCA(nSize, _ATL_SAFE_ALLOCA_DEF_THRESHOLD) );
- if( pCodecs == NULL )
- return E_OUTOFMEMORY;
- status = Gdiplus::GetImageDecoders( nCodecs, nSize, pCodecs );
- BuildCodecFilterString( pCodecs, nCodecs, strExporters, aguidFileTypes, pszAllFilesDescription, dwExclude, chSeparator );
- return( S_OK );
- }
plaincopy
- inline HRESULT CImage::Load( IStream* pStream ) throw()
- {
- if( !InitGDIPlus() )
- {
- return( E_FAIL );
- }
- Gdiplus::Bitmap bmSrc( pStream );
- if( bmSrc.GetLastStatus() != Gdiplus::Ok )
- {
- return( E_FAIL );
- }
- return( CreateFromGdiplusBitmap( bmSrc ) );
- }
- inline HRESULT CImage::Load( LPCTSTR pszFileName ) throw()
- {
- if( !InitGDIPlus() )
- {
- return( E_FAIL );
- }
- Gdiplus::Bitmap bmSrc( (CT2W)pszFileName );
- if( bmSrc.GetLastStatus() != Gdiplus::Ok )
- {
- return( E_FAIL );
- }
- return( CreateFromGdiplusBitmap( bmSrc ) );
- }
plaincopy
- inline HRESULT CImage::Save( IStream* pStream, REFGUID guidFileType ) const throw()
- {
- if( !InitGDIPlus() )
- {
- return( E_FAIL );
- }
- UINT nEncoders;
- UINT nBytes;
- Gdiplus::Status status;
- status = Gdiplus::GetImageEncodersSize( &nEncoders, &nBytes );
- if( status != Gdiplus::Ok )
- {
- return( E_FAIL );
- }
- USES_ATL_SAFE_ALLOCA;
- Gdiplus::ImageCodecInfo* pEncoders = static_cast< Gdiplus::ImageCodecInfo* >( _ATL_SAFE_ALLOCA(nBytes, _ATL_SAFE_ALLOCA_DEF_THRESHOLD) );
- if( pEncoders == NULL )
- return E_OUTOFMEMORY;
- status = Gdiplus::GetImageEncoders( nEncoders, nBytes, pEncoders );
- if( status != Gdiplus::Ok )
- {
- return( E_FAIL );
- }
- CLSID clsidEncoder = FindCodecForFileType( guidFileType, pEncoders, nEncoders );
- if( clsidEncoder == CLSID_NULL )
- {
- return( E_FAIL );
- }
- if( m_bHasAlphaChannel )
- {
- ATLASSUME( m_nBPP == 32 );
- Gdiplus::Bitmap bm( m_nWidth, m_nHeight, m_nPitch, PixelFormat32bppARGB, static_cast< BYTE* >( m_pBits ) );
- status = bm.Save( pStream, &clsidEncoder, NULL );
- if( status != Gdiplus::Ok )
- {
- return( E_FAIL );
- }
- }
- else
- {
- Gdiplus::Bitmap bm( m_hBitmap, NULL );
- status = bm.Save( pStream, &clsidEncoder, NULL );
- if( status != Gdiplus::Ok )
- {
- return( E_FAIL );
- }
- }
- return( S_OK );
- }
- inline HRESULT CImage::Save( LPCTSTR pszFileName, REFGUID guidFileType ) const throw()
- {
- if( !InitGDIPlus() )
- {
- return( E_FAIL );
- }
- UINT nEncoders;
- UINT nBytes;
- Gdiplus::Status status;
- status = Gdiplus::GetImageEncodersSize( &nEncoders, &nBytes );
- if( status != Gdiplus::Ok )
- {
- return( E_FAIL );
- }
- USES_CONVERSION_EX;
- Gdiplus::ImageCodecInfo* pEncoders = static_cast< Gdiplus::ImageCodecInfo* >( _ATL_SAFE_ALLOCA(nBytes, _ATL_SAFE_ALLOCA_DEF_THRESHOLD) );
- if( pEncoders == NULL )
- return E_OUTOFMEMORY;
- status = Gdiplus::GetImageEncoders( nEncoders, nBytes, pEncoders );
- if( status != Gdiplus::Ok )
- {
- return( E_FAIL );
- }
- CLSID clsidEncoder = CLSID_NULL;
- if( guidFileType == GUID_NULL )
- {
- // Determine clsid from extension
- clsidEncoder = FindCodecForExtension( ::PathFindExtension( pszFileName ), pEncoders, nEncoders );
- }
- else
- {
- // Determine clsid from file type
- clsidEncoder = FindCodecForFileType( guidFileType, pEncoders, nEncoders );
- }
- if( clsidEncoder == CLSID_NULL )
- {
- return( E_FAIL );
- }
- LPCWSTR pwszFileName = T2CW_EX( pszFileName, _ATL_SAFE_ALLOCA_DEF_THRESHOLD );
- #ifndef _UNICODE
- if( pwszFileName == NULL )
- return E_OUTOFMEMORY;
- #endif // _UNICODE
- if( m_bHasAlphaChannel )
- {
- ATLASSUME( m_nBPP == 32 );
- Gdiplus::Bitmap bm( m_nWidth, m_nHeight, m_nPitch, PixelFormat32bppARGB, static_cast< BYTE* >( m_pBits ) );
- status = bm.Save( pwszFileName, &clsidEncoder, NULL );
- if( status != Gdiplus::Ok )
- {
- return( E_FAIL );
- }
- }
- else
- {
- Gdiplus::Bitmap bm( m_hBitmap, NULL );
- status = bm.Save( pwszFileName, &clsidEncoder, NULL );
- if( status != Gdiplus::Ok )
- {
- return( E_FAIL );
- }
- }
- return( S_OK );
- }
有上面可知: CImage对象 在第一次Load() 或第一次Save() 时 启动GDI+
以下我们看看 CImage析构时,能否将GDI+关闭掉:
plaincopy
- inline CImage::~CImage() throw()
- {
- Destroy();
- s_initGDIPlus.DecreaseCImageCount();
- }
plaincopy
- inline void CImage::CInitGDIPlus::DecreaseCImageCount() throw()
- {
- EnterCriticalSection(&m_sect);
- if( --m_nCImageObjects == 0 )
- ReleaseGDIPlus();
- LeaveCriticalSection(&m_sect);
- }
plaincopy
- inline void CImage::CInitGDIPlus::ReleaseGDIPlus() throw()
- {
- EnterCriticalSection(&m_sect);
- if( m_dwToken != 0 )
- {
- Gdiplus::GdiplusShutdown( m_dwToken );
- }
- m_dwToken = 0;
- LeaveCriticalSection(&m_sect);
- }
也就是说,一个CImage对象退出时。并不直接关闭GDI+ ,而是只将GDI+使用计数减一。 当其为0时。再关闭GDI+
而这是通过类静态变量来实现计数的:
plaincopy
- static CInitGDIPlus s_initGDIPlus;
由此,我们可作例如以下总结:
当定义多个CImge 变量时, 当某个变量载入图片或保存图片时,启动GDI+。之后。 当其它变量再载入图片或保存时,添加GDI+计数变量
当全部CImage变量都析构完成时,才关闭GDI+。否则,仅仅是降低GDI+计算变量值。
所以说,CImage类是基于GDI+的。