CImage 是基于GDI+的,很老的一篇文章,我很久很久以前看到过的
在许多资料上都说CImage类是基于GDI+的,但是为什么是基于GDI+的呢?
因为使用这个类时,并没有加入#include <gdiplus.h> ,也没有在程序开始和结束时分别写GDI+启动代码GdiplusStartupInput和结束代码GdiplusShutdown
使用这个类时,仅仅需要添加头文件# include<altimage.h>就可以了,比GDI+得使用要简单一些。
而CImage 对图片的处理很类似GDI+ ,其内部是不是封装了GDI+呢? 幸好,CImage类 是源码公开的,我们可以研究其源码,以便加深理解。
首先,看看altimage.h头文件
- #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)
#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的定义:
- 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;
- };
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; };
- static CInitGDIPlus s_initGDIPlus;
static CInitGDIPlus s_initGDIPlus;
- static CDCCache s_cache;
static CDCCache s_cache;
它定义了两个类成员变量: 其中CInitGDIPlus 是负责GDI+的启动和释放
我们再看一下,这个成员类的Init()方法:
- 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;
- }
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+
再看下一个函数:
- inline void CImage::CInitGDIPlus::ReleaseGDIPlus() throw()
- {
- EnterCriticalSection(&m_sect);
- if( m_dwToken != 0 )
- {
- Gdiplus::GdiplusShutdown( m_dwToken );
- }
- m_dwToken = 0;
- LeaveCriticalSection(&m_sect);
- }
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 构造函数
- 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();
- }
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();}
- inline void CImage::CInitGDIPlus::IncreaseCImageCount() throw()
- {
- EnterCriticalSection(&m_sect);
- m_nCImageObjects++;
- LeaveCriticalSection(&m_sect);
- }
inline void CImage::CInitGDIPlus::IncreaseCImageCount() throw(){ EnterCriticalSection(&m_sect); m_nCImageObjects++; LeaveCriticalSection(&m_sect);}
由此可见,构造函数并没有启动GDI+
也就是说定义 CImage image; 这个image变量时,并没有启动GDI+
我们继续查找:
- inline bool CImage::InitGDIPlus() throw()
- {
- bool bSuccess = s_initGDIPlus.Init();
- return( bSuccess );
- }
inline bool CImage::InitGDIPlus() throw(){ bool bSuccess = s_initGDIPlus.Init(); return( bSuccess );}
CImage使用InitGDIPlus() 来初始化GDI+
因此我们查找InitGDIPlus() 的所有引用 ,发现以下函数:
- 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 );
- }
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 );}
- 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 ) );
- }
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 ) );}
- 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 );
- }
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+关闭掉:
- inline CImage::~CImage() throw()
- {
- Destroy();
- s_initGDIPlus.DecreaseCImageCount();
- }
inline CImage::~CImage() throw(){ Destroy(); s_initGDIPlus.DecreaseCImageCount();}
- inline void CImage::CInitGDIPlus::DecreaseCImageCount() throw()
- {
- EnterCriticalSection(&m_sect);
- if( --m_nCImageObjects == 0 )
- ReleaseGDIPlus();
- LeaveCriticalSection(&m_sect);
- }
inline void CImage::CInitGDIPlus::DecreaseCImageCount() throw(){ EnterCriticalSection(&m_sect); if( --m_nCImageObjects == 0 ) ReleaseGDIPlus(); LeaveCriticalSection(&m_sect);}
- inline void CImage::CInitGDIPlus::ReleaseGDIPlus() throw()
- {
- EnterCriticalSection(&m_sect);
- if( m_dwToken != 0 )
- {
- Gdiplus::GdiplusShutdown( m_dwToken );
- }
- m_dwToken = 0;
- LeaveCriticalSection(&m_sect);
- }
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+
而这是通过类静态变量来实现计数的:
- 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+的,很老的一篇文章,我很久很久以前看到过的的更多相关文章
- 转载一篇比较详细的讲解html,css的一篇文章,很长
转载自这里,转载请注明出处. DIV+CSS系统学习笔记回顾 第一部分 HTML 第一章 职业规划和前景 职业方向规划定位: web前端开发工程师 web网站架构师 自己创业 转岗管理或其他 ...
- compass和sass很好的两篇文章
Sass是一种"CSS预处理器",可以让CSS的开发变得简单和可维护.但是,只有搭配Compass,它才能显出真正的威力. 本文介绍Compass的用法.毫不夸张地说,学会了Com ...
- 对C#泛型讲的很好的一篇文章
请参考 https://www.cnblogs.com/kissdodog/archive/2013/01/27/2879185.html
- [转]C#中基于GDI+(Graphics)图像处理系列之前言
直接给出原文链接吧: C#中基于GDI+(Graphics)图像处理系列之前言 链接:https://pan.baidu.com/s/1zm5TCOHqkqEfiLZuqO0UMA 提取码:qz0h
- 基于Vue的Quasar Framework 介绍 这个框架UI组件很全面
基于Vue的Quasar Framework 介绍 这个框架UI组件很全面 基于Vue的Quasar Framework 中文网http://www.quasarchs.com/ quasarfram ...
- windows下用纯C实现一个简陋的imshow:基于GDI
intro 先前实现了GDI显示图像时设定窗口大小为图像大小,不过并没有刻意封装函数调用接口,并不适合给其他函数调用.现在简单封装一下,特点: 纯C 基于GDI,因此只支持windows平台 类似于o ...
- 基于GDI显示png图像
intro 先前基于GDI已经能够显示BITMAP图像:windows下控制台程序实现窗口显示 ,其中BMP图像是使用LoadImage()这一Win32 API函数来做的.考虑到LoadImage( ...
- logstash快速入门 (这篇文章很不错 ) | 两种方式往logstash传输数据实例:Apache 日志(从文件获取)、Syslog方式
原文地址:http://www.2cto.com/os/201411/352015.html 原文地址:http://logstash.net/docs/1.4.2/tutorials/getting ...
- 【转帖】我以为我对Kafka很了解,直到我看了这篇文章
我以为我对Kafka很了解,直到我看了这篇文章 2019-08-12 18:05 https://www.sohu.com/a/333235171_463994?spm=smpc.author.fd- ...
随机推荐
- PAT甲级——A1104 Sum of Number Segments【20】
Consider a positive integer N written in standard notation with k+1 digits ai as ak⋯a1a0 ...
- 主页面与iframe页面之间的javascript函数的调用
1:在主页面里调用iframe页里面的javascript函数 <script type="text/javascript"> var childWindow = $( ...
- shell脚本练习06
######################################################################### # File Name: -.sh # Author ...
- css3 随记
1 让子元素对其的方式 box-pack 2 -webkit-text-size-adjust 解决字体大小失效问题http://www.frontopen.com/273.html 3 disp ...
- linux 解压 WinRAR 压缩文件
1.Download rar for linux wget http://www.rarlab.com/rar/rarlinux-x64-5.5.b1.tar.gz 2.Configure rar t ...
- thinkphp Mongo模型
Mongo模型是专门为Mongo数据库驱动而支持的Model扩展,如果需要操作Mongo数据库的话,自定义的模型类必须继承Think\Model\MongoModel. Mongo模型为操作Mongo ...
- 带权二分图——KM算法hdu2255 poj3565
进阶指南的板子好像有点问题..交到hdu上会T 需要了解的一些概念: 交错树,顶标,修改量 #include<iostream> #include<stdio.h> #incl ...
- Ubuntu usb设备端口号绑定
1.将串口设备插入USB口,通过lsusb查看端口信息.例如: ID 1a86:7523 表示usb设备的ID(这个ID由芯片制造商设置,可以唯一表示该设备) 1a86 usb_device_desc ...
- 纯CSS样式写刘海屏效果
1. 效果: 2. 代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset=& ...
- 通过挂钩NtCreateSection监控可执行模块
通过挂钩 NtCreateSection 监控可执行模块 在 Win32 中,我们使用 CreateFileMapping 来创建映射文件对象,函数原型如下: HANDLE CreateFileMap ...