ATL::CImage 的透明通道的处理与格式转换
1、透明通道的处理:
在 CImage 的处理中,(其实就是 Windows 中的位图处理)一切的绘图函数都不修改透明通道的值,即使你进行 TransparentBlt 调用(关于该函数很有意思,在不包含透明通道的位图处理中,进行透明色的过滤处理,否则直接复制透明通道到目标DC),或者你使用 AlpahBlend ,更是直接对 RGB 三原色进行混合运算,而不会对透明通道进行任何的处理,换句话说,在32位的 RGBA 格式位图中,所有的 GDI 函数都是将位图当做 RGBX 格式进行处理的;
因此创建一个包含透明通道的32位位图,进行绘图,然后保存为JPG格式,看起来没有任何问题,因为 JPG 损失了透明通道,如果保存为 PNG 格式,那么你将什么都看不到,因为透明通道的值全部是 0 , 就是说整张图都是透明的;
有鉴于此,在保存成 PNG 格式的图时,如果要求透明效果,那么必须手工的对透明通道进行处理,因此在生成图的时候需要将将来透明的部分使用一个不使用的颜色进行填充,作为未来的透明色;然后使用 GDI 生成图,例如绘制一张的地图,然后将所有RGB等于透明色的像素的透明通道设置为 0 ,其他像素的透明通道设置为 0xFF , 然后保存为 PNG 格式,那么就该PNG图就可以放置到其他图的上面进行透明显示了,例如在底下放置航拍的地貌栅格图;该处理函数大致如下:
{
int nPitch = Image.GetPitch( ) , nBPP = Image.GetBPP( );
LPBYTE lpBits = reinterpret_cast< LPBYTE>( Image.GetBits( ) );
CHK_EXP_RET( lpBits == NULL || nPitch == 0 || nBPP != 32 , -1 );
DWORD COLOR_MASK = 0x00FFFFFF , ALPHA_BITS = Alpha << 24 ;
for( int yPos = 0 ; yPos < Image.GetHeight( ) ; yPos ++ )
{
LPBYTE lpBytes = lpBits + ( yPos * nPitch );
PDWORD lpLines = reinterpret_cast< PDWORD >( lpBytes );
for( int xPos = 0 ; xPos < Image.GetWidth( ) ; xPos ++ )
{
lpLines[ xPos ] &= COLOR_MASK ;
if( lpLines[ xPos ] != Transparent )
{
lpLines[ xPos ] |= ALPHA_MASK ;
}
else
{
lpLines[ xPos ] |= ALPHA_BITS ;
}
}
}
return ERR_SUCCESS;
}
2、CImage 类可以创建一个包含透明通道的32位位图,但是如果你 ATTACH 一个 32 位的位图却无法设置透明通道属性,因为该类没有提供相应的函数,所以需要自己继承一个类来增加函数进行处理,当然,MSDN 上已经说明,在 VS2010 中已经解决了这个 BUG
3、如何将 CImage 保存到内存块中,原理是创建一个非固定大小的 HGLOBAL ,然后在该句柄上创建 IStream 接口,调用 CImage::Save 函数即可,可以参考下面的代码:
{
//ISF_Trace_Time_Spend Time_Spender( "Windows_Image_Convert::WriteImageData");
REFGUID ImageFormat = GetImageFormat( Image_MIME_Type , ImageFormatPNG );
Windows_Global_Memory hGlobal( GMEM_MOVEABLE , 0 );
CHK_EXP_RET( hGlobal.Handler( ) == NULL , ERR_INVALID );
CComQIPtr< IStream > spImageStream;
CreateStreamOnHGlobal( hGlobal , TRUE , & spImageStream );
CHK_EXP_RET( ! spImageStream , ERR_INVALID );
if( FAILED( Bitmap.Save( spImageStream , ImageFormat ) ) )
{
ISF_ERROR( "Save Image To Stream Failed" , NULL );
return ERR_INVALID ;
}
LARGE_INTEGER ImageBegin = { 0 , 0 };
spImageStream->Seek( ImageBegin , STREAM_SEEK_SET , NULL );
SIZE_T Image_Data_Size = GlobalSize( hGlobal.Handler( ) ) ;
CHK_EXP_RET( Image_Data.size( Image_Data_Size ) == -1 , -1 );
memcpy( Image_Data.base( ) , hGlobal.Lock( ) , Image_Data_Size );
Image_Data.wr_ptr( Image_Data_Size );
return ERR_SUCCESS ;
}
其中使用到了 Windows_Global_Memory 类,该类是一个简单的 HGLOBAL 的管理类,声明和代码如下:
// 全局内存的申请和释放管理类
struct Windows_Global_Memory
{
//---------------------------------------------------------------------------------------------
// Summary:
// Constructor / Destructor
//---------------------------------------------------------------------------------------------
Windows_Global_Memory( UINT uFlags = GMEM_MOVEABLE , SIZE_T dwBytes = 0 );
~Windows_Global_Memory( );
//---------------------------------------------------------------------------------------------
// Summary:
// Lock & Unlock Memory
//---------------------------------------------------------------------------------------------
LPVOID Lock( );
void UnLock( );
//---------------------------------------------------------------------------------------------
// Summary:
// Operator & Handler
//---------------------------------------------------------------------------------------------
operator HGLOBAL( );
HGLOBAL Handler( );
//---------------------------------------------------------------------------------------------
HGLOBAL m_hGlobal ;
LPVOID m_lpData ;
};
inline Windows_Global_Memory::Windows_Global_Memory( UINT uFlags , SIZE_T dwBytes )
{
m_hGlobal = ::GlobalAlloc( uFlags , dwBytes ); m_lpData = NULL ;
}
inline Windows_Global_Memory::~Windows_Global_Memory( )
{
UnLock( );
GlobalFree( m_hGlobal );
}
inline LPVOID Windows_Global_Memory::Lock( )
{
CHK_EXP_RET( m_lpData != NULL , m_lpData );
return m_lpData = ::GlobalLock( m_hGlobal );
}
inline void Windows_Global_Memory::UnLock( )
{
CHK_EXP_END( m_lpData == NULL );
::GlobalUnlock( m_hGlobal );
m_lpData = NULL;
}
inline Windows_Global_Memory::operator HGLOBAL( )
{
return m_hGlobal;
}
inline HGLOBAL Windows_Global_Memory::Handler( )
{
return m_hGlobal;
}
还有函数 GetImageFormat , 该函数就是根据输入的MIME格式给出对应的 GUID,例如给出 HTTP_MIME_IMAGE_PNG 就返回 ImageFormatPNG