GDI+ 总结二 : 为什么说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)


上面包含了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;
	};


 

         static CInitGDIPlus s_initGDIPlus;

 

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


也就是说 使用这个函数 启动GDI+

 

再看下一个函数:

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


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


 

有上面可知: CImage对象 在第一次Load() 或第一次Save() 时 启动GDI+

 

下面我们看看 CImage析构时,是否能将GDI+关闭掉:

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


 

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

 

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

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

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

 

 

 

 

 

你可能感兴趣的:(cache,image,null,Class,byte,GDI+)