在许多资料上都说CImage类是基于GDI+的,但是为什么是基于GDI+的呢?

因为使用这个类时,并没有加入#include <gdiplus.h> ,也没有在程序开始和结束时分别写GDI+启动代码GdiplusStartupInput和结束代码GdiplusShutdown

使用这个类时,仅仅需要添加头文件# include<altimage.h>就可以了,比GDI+得使用要简单一些。

而CImage 对图片的处理很类似GDI+ ,其内部是不是封装了GDI+呢? 幸好,CImage类 是源码公开的,我们可以研究其源码,以便加深理解。

首先,看看altimage.h头文件

 
  1. #ifndef __ATLIMAGE_H__
  2. #define __ATLIMAGE_H__
  3. #pragma once
  4. #include <atldef.h>
  5. #include <atlbase.h>
  6. #include <atlstr.h>
  7. #include <atlsimpcoll.h>
  8. #include <atltypes.h>
  9. #ifndef _ATL_NO_PRAGMA_WARNINGS
  10. #pragma warning (push)
  11. #pragma warning(disable : 4820) // padding added after member
  12. #endif //!_ATL_NO_PRAGMA_WARNINGS
  13. #pragma warning( push, 3 )
  14. #pragma push_macro("new")
  15. #undef new
  16. #include <gdiplus.h>   // 注意这里:添加了GDI+得头文件
  17. #pragma pop_macro("new")
  18. #pragma warning( pop )
  19. #include <shlwapi.h>
  20. #ifndef _ATL_NO_DEFAULT_LIBS
  21. #pragma comment(lib, "gdi32.lib")
  22. #pragma comment(lib, "shlwapi.lib")
  23. #pragma comment(lib, "gdiplus.lib")
  24. #if WINVER >= 0x0500
  25. #pragma comment(lib, "msimg32.lib")
  26. #endif  // WINVER >= 0x0500
  27. #endif  // !_ATL_NO_DEFAULT_LIBS
  28. #pragma pack(push, _ATL_PACKING)

#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的定义:

 
  1. class CImage
  2. {
  3. private:
  4. class CDCCache
  5. {
  6. public:
  7. CDCCache() throw();
  8. ~CDCCache() throw();
  9. HDC GetDC() throw();
  10. void ReleaseDC( HDC ) throw();
  11. private:
  12. HDC m_ahDCs[CIMAGE_DC_CACHE_SIZE];
  13. };
  14. class CInitGDIPlus
  15. {
  16. public:
  17. CInitGDIPlus() throw();
  18. ~CInitGDIPlus() throw();
  19. bool Init() throw();
  20. void ReleaseGDIPlus() throw();
  21. void IncreaseCImageCount() throw();
  22. void DecreaseCImageCount() throw();
  23. private:
  24. ULONG_PTR m_dwToken;
  25. CRITICAL_SECTION m_sect;
  26. LONG m_nCImageObjects;
  27. };

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; };

 
  1. static CInitGDIPlus s_initGDIPlus;

static CInitGDIPlus s_initGDIPlus;

 
  1. static CDCCache s_cache;

static CDCCache s_cache;

它定义了两个类成员变量: 其中CInitGDIPlus 是负责GDI+的启动和释放

我们再看一下,这个成员类的Init()方法:

 
  1. inline bool CImage::CInitGDIPlus::Init() throw()
  2. {
  3. EnterCriticalSection(&m_sect);
  4. bool fRet = true;
  5. if( m_dwToken == 0 )
  6. {
  7. Gdiplus::GdiplusStartupInput input;
  8. Gdiplus::GdiplusStartupOutput output;
  9. Gdiplus::Status status = Gdiplus::GdiplusStartup( &m_dwToken, &input, &output );   //启动GDI+
  10. if( status != Gdiplus::Ok )
  11. fRet = false;
  12. }
  13. LeaveCriticalSection(&m_sect);
  14. return fRet;
  15. }

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+

再看下一个函数:

 
  1. inline void CImage::CInitGDIPlus::ReleaseGDIPlus() throw()
  2. {
  3. EnterCriticalSection(&m_sect);
  4. if( m_dwToken != 0 )
  5. {
  6. Gdiplus::GdiplusShutdown( m_dwToken );
  7. }
  8. m_dwToken = 0;
  9. LeaveCriticalSection(&m_sect);
  10. }

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 构造函数

 
  1. inline CImage::CImage() throw() :
  2. m_hBitmap( NULL ),
  3. m_pBits( NULL ),
  4. m_hDC( NULL ),
  5. m_nDCRefCount( 0 ),
  6. m_hOldBitmap( NULL ),
  7. m_nWidth( 0 ),
  8. m_nHeight( 0 ),
  9. m_nPitch( 0 ),
  10. m_nBPP( 0 ),
  11. m_iTransparentColor( -1 ),
  12. m_bHasAlphaChannel( false ),
  13. m_bIsDIBSection( false )
  14. {
  15. s_initGDIPlus.IncreaseCImageCount();
  16. }

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();}

 
  1. inline void CImage::CInitGDIPlus::IncreaseCImageCount() throw()
  2. {
  3. EnterCriticalSection(&m_sect);
  4. m_nCImageObjects++;
  5. LeaveCriticalSection(&m_sect);
  6. }

inline void CImage::CInitGDIPlus::IncreaseCImageCount() throw(){ EnterCriticalSection(&m_sect); m_nCImageObjects++; LeaveCriticalSection(&m_sect);}

由此可见,构造函数并没有启动GDI+

也就是说定义  CImage image;  这个image变量时,并没有启动GDI+

我们继续查找:

 
  1. inline bool CImage::InitGDIPlus() throw()
  2. {
  3. bool bSuccess = s_initGDIPlus.Init();
  4. return( bSuccess );
  5. }

inline bool CImage::InitGDIPlus() throw(){ bool bSuccess = s_initGDIPlus.Init(); return( bSuccess );}

CImage使用InitGDIPlus() 来初始化GDI+

因此我们查找InitGDIPlus() 的所有引用 ,发现以下函数:

 
  1. inline HRESULT CImage::GetImporterFilterString( CSimpleString& strImporters,
  2. CSimpleArray< GUID >& aguidFileTypes, LPCTSTR pszAllFilesDescription /* = NULL */,
  3. DWORD dwExclude /* = excludeDefaultLoad */, TCHAR chSeparator /* = '|' */ )
  4. {
  5. if( !InitGDIPlus() )
  6. {
  7. return( E_FAIL );
  8. }
  9. UINT nCodecs;
  10. UINT nSize;
  11. Gdiplus::Status status;
  12. Gdiplus::ImageCodecInfo* pCodecs;
  13. status = Gdiplus::GetImageDecodersSize( &nCodecs, &nSize );
  14. USES_ATL_SAFE_ALLOCA;
  15. pCodecs = static_cast< Gdiplus::ImageCodecInfo* >( _ATL_SAFE_ALLOCA(nSize, _ATL_SAFE_ALLOCA_DEF_THRESHOLD) );
  16. if( pCodecs == NULL )
  17. return E_OUTOFMEMORY;
  18. status = Gdiplus::GetImageDecoders( nCodecs, nSize, pCodecs );
  19. BuildCodecFilterString( pCodecs, nCodecs, strImporters, aguidFileTypes, pszAllFilesDescription, dwExclude, chSeparator );
  20. return( S_OK );
  21. }
  22. inline HRESULT CImage::GetExporterFilterString( CSimpleString& strExporters,
  23. CSimpleArray< GUID >& aguidFileTypes, LPCTSTR pszAllFilesDescription /* = NULL */,
  24. DWORD dwExclude /* = excludeDefaultSave */, TCHAR chSeparator /* = '|' */ )
  25. {
  26. if( !InitGDIPlus() )
  27. {
  28. return( E_FAIL );
  29. }
  30. UINT nCodecs;
  31. UINT nSize;
  32. Gdiplus::Status status;
  33. Gdiplus::ImageCodecInfo* pCodecs;
  34. status = Gdiplus::GetImageDecodersSize( &nCodecs, &nSize );
  35. USES_ATL_SAFE_ALLOCA;
  36. pCodecs = static_cast< Gdiplus::ImageCodecInfo* >( _ATL_SAFE_ALLOCA(nSize, _ATL_SAFE_ALLOCA_DEF_THRESHOLD) );
  37. if( pCodecs == NULL )
  38. return E_OUTOFMEMORY;
  39. status = Gdiplus::GetImageDecoders( nCodecs, nSize, pCodecs );
  40. BuildCodecFilterString( pCodecs, nCodecs, strExporters, aguidFileTypes, pszAllFilesDescription, dwExclude, chSeparator );
  41. return( S_OK );
  42. }

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 );}

 
  1. inline HRESULT CImage::Load( IStream* pStream ) throw()
  2. {
  3. if( !InitGDIPlus() )
  4. {
  5. return( E_FAIL );
  6. }
  7. Gdiplus::Bitmap bmSrc( pStream );
  8. if( bmSrc.GetLastStatus() != Gdiplus::Ok )
  9. {
  10. return( E_FAIL );
  11. }
  12. return( CreateFromGdiplusBitmap( bmSrc ) );
  13. }
  14. inline HRESULT CImage::Load( LPCTSTR pszFileName ) throw()
  15. {
  16. if( !InitGDIPlus() )
  17. {
  18. return( E_FAIL );
  19. }
  20. Gdiplus::Bitmap bmSrc( (CT2W)pszFileName );
  21. if( bmSrc.GetLastStatus() != Gdiplus::Ok )
  22. {
  23. return( E_FAIL );
  24. }
  25. return( CreateFromGdiplusBitmap( bmSrc ) );
  26. }

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 ) );}

 
  1. inline HRESULT CImage::Save( IStream* pStream, REFGUID guidFileType ) const throw()
  2. {
  3. if( !InitGDIPlus() )
  4. {
  5. return( E_FAIL );
  6. }
  7. UINT nEncoders;
  8. UINT nBytes;
  9. Gdiplus::Status status;
  10. status = Gdiplus::GetImageEncodersSize( &nEncoders, &nBytes );
  11. if( status != Gdiplus::Ok )
  12. {
  13. return( E_FAIL );
  14. }
  15. USES_ATL_SAFE_ALLOCA;
  16. Gdiplus::ImageCodecInfo* pEncoders = static_cast< Gdiplus::ImageCodecInfo* >( _ATL_SAFE_ALLOCA(nBytes, _ATL_SAFE_ALLOCA_DEF_THRESHOLD) );
  17. if( pEncoders == NULL )
  18. return E_OUTOFMEMORY;
  19. status = Gdiplus::GetImageEncoders( nEncoders, nBytes, pEncoders );
  20. if( status != Gdiplus::Ok )
  21. {
  22. return( E_FAIL );
  23. }
  24. CLSID clsidEncoder = FindCodecForFileType( guidFileType, pEncoders, nEncoders );
  25. if( clsidEncoder == CLSID_NULL )
  26. {
  27. return( E_FAIL );
  28. }
  29. if( m_bHasAlphaChannel )
  30. {
  31. ATLASSUME( m_nBPP == 32 );
  32. Gdiplus::Bitmap bm( m_nWidth, m_nHeight, m_nPitch, PixelFormat32bppARGB, static_cast< BYTE* >( m_pBits ) );
  33. status = bm.Save( pStream, &clsidEncoder, NULL );
  34. if( status != Gdiplus::Ok )
  35. {
  36. return( E_FAIL );
  37. }
  38. }
  39. else
  40. {
  41. Gdiplus::Bitmap bm( m_hBitmap, NULL );
  42. status = bm.Save( pStream, &clsidEncoder, NULL );
  43. if( status != Gdiplus::Ok )
  44. {
  45. return( E_FAIL );
  46. }
  47. }
  48. return( S_OK );
  49. }
  50. inline HRESULT CImage::Save( LPCTSTR pszFileName, REFGUID guidFileType ) const throw()
  51. {
  52. if( !InitGDIPlus() )
  53. {
  54. return( E_FAIL );
  55. }
  56. UINT nEncoders;
  57. UINT nBytes;
  58. Gdiplus::Status status;
  59. status = Gdiplus::GetImageEncodersSize( &nEncoders, &nBytes );
  60. if( status != Gdiplus::Ok )
  61. {
  62. return( E_FAIL );
  63. }
  64. USES_CONVERSION_EX;
  65. Gdiplus::ImageCodecInfo* pEncoders = static_cast< Gdiplus::ImageCodecInfo* >( _ATL_SAFE_ALLOCA(nBytes, _ATL_SAFE_ALLOCA_DEF_THRESHOLD) );
  66. if( pEncoders == NULL )
  67. return E_OUTOFMEMORY;
  68. status = Gdiplus::GetImageEncoders( nEncoders, nBytes, pEncoders );
  69. if( status != Gdiplus::Ok )
  70. {
  71. return( E_FAIL );
  72. }
  73. CLSID clsidEncoder = CLSID_NULL;
  74. if( guidFileType == GUID_NULL )
  75. {
  76. // Determine clsid from extension
  77. clsidEncoder = FindCodecForExtension( ::PathFindExtension( pszFileName ), pEncoders, nEncoders );
  78. }
  79. else
  80. {
  81. // Determine clsid from file type
  82. clsidEncoder = FindCodecForFileType( guidFileType, pEncoders, nEncoders );
  83. }
  84. if( clsidEncoder == CLSID_NULL )
  85. {
  86. return( E_FAIL );
  87. }
  88. LPCWSTR pwszFileName = T2CW_EX( pszFileName, _ATL_SAFE_ALLOCA_DEF_THRESHOLD );
  89. #ifndef _UNICODE
  90. if( pwszFileName == NULL )
  91. return E_OUTOFMEMORY;
  92. #endif // _UNICODE
  93. if( m_bHasAlphaChannel )
  94. {
  95. ATLASSUME( m_nBPP == 32 );
  96. Gdiplus::Bitmap bm( m_nWidth, m_nHeight, m_nPitch, PixelFormat32bppARGB, static_cast< BYTE* >( m_pBits ) );
  97. status = bm.Save( pwszFileName, &clsidEncoder, NULL );
  98. if( status != Gdiplus::Ok )
  99. {
  100. return( E_FAIL );
  101. }
  102. }
  103. else
  104. {
  105. Gdiplus::Bitmap bm( m_hBitmap, NULL );
  106. status = bm.Save( pwszFileName, &clsidEncoder, NULL );
  107. if( status != Gdiplus::Ok )
  108. {
  109. return( E_FAIL );
  110. }
  111. }
  112. return( S_OK );
  113. }

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+关闭掉:

 
  1. inline CImage::~CImage() throw()
  2. {
  3. Destroy();
  4. s_initGDIPlus.DecreaseCImageCount();
  5. }

inline CImage::~CImage() throw(){ Destroy(); s_initGDIPlus.DecreaseCImageCount();}

 
  1. inline void CImage::CInitGDIPlus::DecreaseCImageCount() throw()
  2. {
  3. EnterCriticalSection(&m_sect);
  4. if( --m_nCImageObjects == 0 )
  5. ReleaseGDIPlus();
  6. LeaveCriticalSection(&m_sect);
  7. }

inline void CImage::CInitGDIPlus::DecreaseCImageCount() throw(){ EnterCriticalSection(&m_sect); if( --m_nCImageObjects == 0 ) ReleaseGDIPlus(); LeaveCriticalSection(&m_sect);}

 
  1. inline void CImage::CInitGDIPlus::ReleaseGDIPlus() throw()
  2. {
  3. EnterCriticalSection(&m_sect);
  4. if( m_dwToken != 0 )
  5. {
  6. Gdiplus::GdiplusShutdown( m_dwToken );
  7. }
  8. m_dwToken = 0;
  9. LeaveCriticalSection(&m_sect);
  10. }

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+

而这是通过类静态变量来实现计数的:

 
  1. static CInitGDIPlus s_initGDIPlus;

static CInitGDIPlus s_initGDIPlus;

由此,我们可作如下总结:

当定义多个CImge 变量时, 当某个变量加载图片或保存图片时,启动GDI+,之后, 当其他变量再加载图片或保存时,增加GDI+计数变量

当所有CImage变量都析构完毕时,才关闭GDI+,否则,只是减少GDI+计算变量值。

所以说,CImage类是基于GDI+的。

原文地址:http://blog.csdn.net/shuilan0066/article/details/7086371

我不喜欢做界面,所以就看过一次,因为我不会,所以有个印象。

CImage 是 GDI+ 里的东西

CImage 是基于GDI+的,很老的一篇文章,我很久很久以前看到过的的更多相关文章

  1. 转载一篇比较详细的讲解html,css的一篇文章,很长

      转载自这里,转载请注明出处. DIV+CSS系统学习笔记回顾   第一部分 HTML 第一章 职业规划和前景 职业方向规划定位: web前端开发工程师 web网站架构师 自己创业 转岗管理或其他 ...

  2. compass和sass很好的两篇文章

    Sass是一种"CSS预处理器",可以让CSS的开发变得简单和可维护.但是,只有搭配Compass,它才能显出真正的威力. 本文介绍Compass的用法.毫不夸张地说,学会了Com ...

  3. 对C#泛型讲的很好的一篇文章

    请参考 https://www.cnblogs.com/kissdodog/archive/2013/01/27/2879185.html

  4. [转]C#中基于GDI+(Graphics)图像处理系列之前言

    直接给出原文链接吧: C#中基于GDI+(Graphics)图像处理系列之前言 链接:https://pan.baidu.com/s/1zm5TCOHqkqEfiLZuqO0UMA 提取码:qz0h

  5. 基于Vue的Quasar Framework 介绍 这个框架UI组件很全面

    基于Vue的Quasar Framework 介绍 这个框架UI组件很全面 基于Vue的Quasar Framework 中文网http://www.quasarchs.com/ quasarfram ...

  6. windows下用纯C实现一个简陋的imshow:基于GDI

    intro 先前实现了GDI显示图像时设定窗口大小为图像大小,不过并没有刻意封装函数调用接口,并不适合给其他函数调用.现在简单封装一下,特点: 纯C 基于GDI,因此只支持windows平台 类似于o ...

  7. 基于GDI显示png图像

    intro 先前基于GDI已经能够显示BITMAP图像:windows下控制台程序实现窗口显示 ,其中BMP图像是使用LoadImage()这一Win32 API函数来做的.考虑到LoadImage( ...

  8. logstash快速入门 (这篇文章很不错 ) | 两种方式往logstash传输数据实例:Apache 日志(从文件获取)、Syslog方式

    原文地址:http://www.2cto.com/os/201411/352015.html 原文地址:http://logstash.net/docs/1.4.2/tutorials/getting ...

  9. 【转帖】我以为我对Kafka很了解,直到我看了这篇文章

    我以为我对Kafka很了解,直到我看了这篇文章 2019-08-12 18:05 https://www.sohu.com/a/333235171_463994?spm=smpc.author.fd- ...

随机推荐

  1. 使用maven搭建Hibernate

    使用maven搭建Hibernate框架(web项目) create table USERS ( ID NUMBER not null primary key, NAME ), PASSWORD ), ...

  2. assert(断言)

    Python assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常. 语法格式: assert expression 等价于: if not expression: ra ...

  3. [JZOJ5355] 【NOIP2017提高A组模拟9.9】保命

    题目 描述 题目已经足够清晰了,所以不再赘述题目大意. 思考历程 一眼看下去,好像是一道大水题! 然而,再看几眼,感觉又不是一道水题! 然后想了半天,感觉它特别难转移! 最终打了一个暴力,然后发现样例 ...

  4. 修改Apache的默认站点目录的方法,需要的朋友可以参考下

    由于博客系统崩了,所以要考虑重新建立博客系统,首先遇到的一个问题就是原来的博客系统是安装一个独立的磁盘上面的(http://m.0834jl.com)不是安装在系统盘上面的,然而一般在linux下面安 ...

  5. 0821NOIP模拟测试赛后总结

    60分rank20.挂.完. 赛时状态 不是很好.老眼混花看错无数题目信息. 倒不是很困.尽管昨天晚上为了某个该死的s-h-s-j活动报告忙到了今天,但我不得不说车上的睡眠还是挺好的. 照例通读三道题 ...

  6. Win10弹出需要管理员权限才能删除文件夹,解决办法

    Win键+R(就是开始-运行),弹出的输入框输入gpedit.msc回车. 绿色圈内是正解,设置为已禁用.已禁用.已禁用.记着重启才生效.

  7. 19-10-29-Night-X

    布谷. 欢迎大家来不耻下问. 这里是正解不会,暴力写跪,乱搞鸡肋的某虻 . 好想放假啊!!!! 话说猫厂现在产量低迷…… ZJ一下: T1,三维偏序,只码了$\Theta(N^2)$暴力 T2,暴力愉 ...

  8. 如何用maven读取本地架包

    一.这篇微博是建立在已经把架包复制过来的情况下的.其余情况另外谈. 二.本篇文章用的是cmd控制的 maven的安装请看以下链接:https://jingyan.baidu.com/article/3 ...

  9. 03. 将pdb调试文件包含到.vsix包中

    vs插件如何把pdb文件打包进去,方便记录日志和调试 <PropertyGroup> <CopyLocalLockFileAssemblies>true</CopyLoc ...

  10. Jeecg-Boot前后端分离,针对敏感数据,加密传递方案

    # 针对敏感数据,加密传递方案 第一步: 在vue页面引入aesEncrypt.js encryption方法.示例代码: import { encryption } from '@/utils/en ...