Wince/VC高效PNG貼圖,自定義Alpha算法

工做中,作一些炫點的界面都須要用到PNG圖片,Wince裏面微軟也提供了PNG圖片的支持,不過Alpha的混合速度比較慢,因此本身實現了一個Alpha的混合運算接口,通過測試,要比微軟AlphaBlend快四、5倍。固然Alpha混合的方法也適合window下的VC使用。下面有測試的數據。html

原創博文,須要轉載,請標明出處:http://www.cnblogs.com/mythou/p/3150396.html算法

 

一、建立兼容32位位圖。函數

    通常界面貼圖,咱們都是使用微軟的兼容DC和兼容位圖進行處理。不過這裏我須要建立一張32位的設備無關位圖。(有關DIB位圖相關知識,不瞭解的能夠百度一下,這是和兼容位圖對應的一種位圖格式。這裏就很少說了)。測試

/*
*hDC:兼容的DC指針
*nWidth:須要建立DIB位圖寬
*nHeight:建立位圖高
*pBitmapData:這是我本身定義的數據類型,就是一個unsigned char * 類型
*/

HBITMAP CPngBitBlt::Create32Bitmap(HDC hDC, int nWidth, int nHeight,HBMDC &pBitmapData) { if(hDC == NULL) { return NULL; } if(nWidth==0 || nHeight==0) { return NULL; } BITMAPINFO *pbinfo = new BITMAPINFO; if(!pbinfo) { return NULL; } ZeroMemory(pbinfo, sizeof(BITMAPINFO)); pbinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pbinfo->bmiHeader.biWidth = nWidth; pbinfo->bmiHeader.biHeight = -nHeight;//位圖翻轉 pbinfo->bmiHeader.biPlanes = 1; pbinfo->bmiHeader.biBitCount = 32;//32位位圖 BYTE* lpBitmapBits = NULL; HBITMAP hDIBmp = CreateDIBSection(hDC, pbinfo, DIB_RGB_COLORS, (void **)&lpBitmapBits, NULL, 0); pBitmapData = lpBitmapBits; delete pbinfo; if(hDIBmp == NULL) { return NULL; } return hDIBmp; }

上面的函數是建立一張指定大小的DIB位圖,並且返回這張位圖的數據指針(指向位圖數據的首指針)。優化

這張位圖,通常就是用來當作背景位圖,咱們通常貼圖使用的雙緩衝方式,須要先後兩張位圖,這個DIB位圖就是用作內存DC的位圖。spa

 

二、加載PNG圖片指針

PNG圖片加載,這裏仍是使用微軟的IImage解碼PNG圖片,使用過pnglib解碼庫,實際解碼速度還比不上IImage,也多是我編譯的libpng庫沒有作優化吧。因此暫時放棄使用libpng。後續有時間會研究一下libpng解碼的原理和嵌入式上優化方式。Android上也是使用libpng解碼png,因此解碼速度應該仍是能夠的,不過估計須要作一些嵌入式平臺的化,比較大部分嵌入式平臺機器CPU主頻都不高,(如今的手機例外 -_-!),我工做接觸的通常也就400M到800M的範圍。下面是解碼部分代碼,主要是從IImage解碼PNG圖片,而後把PNG圖片構造一個DIB位圖,獲取一個能夠操做圖片的指針。code

BOOL CImageData::LoadImageFromFile(const TCHAR *fileName) { // 參數有效性
    if (fileName == NULL) { return FALSE; } // 建立COM實例
    HRESULT hr = NULL; if(FAILED(hr = ::CoCreateInstance(CLSID_ImagingFactory,NULL,CLSCTX_INPROC_SERVER,IID_IImagingFactory,(void**) &m_pImagingFactory))) { return FALSE; }// 從文件中建立圖片
    if(FAILED(hr = m_pImagingFactory->CreateImageFromFile(fileName, &m_pImage))) { return FALSE; } // 獲得圖片信息
    if(FAILED(hr = m_pImage->GetImageInfo(&m_ImageInfo))) { return FALSE; } // 獲取原始數據
    if (ReadImageData() == FALSE) { return FALSE; }   //......// 成功得到圖片信息
    return TRUE; }
BOOL CImageData::ReadImageData() { // 用於返回結果
    BOOL bRet = TRUE; // 參數有效性
    if (m_pImage==NULL || m_pImagingFactory==NULL) { return FALSE; } // 取得圖片原始數據
    RECT rect = {0, 0, m_ImageInfo.Width, m_ImageInfo.Height}; BitmapData bitmapData; bitmapData.Width = m_ImageInfo.Width; bitmapData.Height = m_ImageInfo.Height; bitmapData.PixelFormat = m_ImageInfo.PixelFormat; IBitmapImage *pBitmapImage = NULL; m_pImagingFactory->CreateBitmapFromImage(m_pImage, m_ImageInfo.Width, m_ImageInfo.Height, PIXFMT_32BPP_ARGB, InterpolationHintDefault, &pBitmapImage); pBitmapImage->LockBits(&rect, ImageLockModeRead, PIXFMT_32BPP_ARGB, &bitmapData); // 釋放舊數據
    if (m_pImgDataBuf != NULL) { delete[] m_pImgDataBuf; m_pImgDataBuf = NULL; } // 申請新空間
    bRet = TRUE; m_pImgDataBuf = new unsigned char[m_ImageInfo.Width * m_ImageInfo.Height * 4]; if (m_pImgDataBuf == NULL) { bRet = FALSE; goto ERROR_END_FUNCTION; } // 拷貝數據
    bRet = TRUE; memcpy(m_pImgDataBuf, bitmapData.Scan0, m_ImageInfo.Width * m_ImageInfo.Height * 4); ERROR_END_FUNCTION: pBitmapImage->UnlockBits(&bitmapData); pBitmapImage->Release(); return bRet; }

 

上面主要m_pImgDataBuf 就是一個Byte *的指針,一個指向PNG圖片數據的起始指針。咱們利用這個指針就能夠操做咱們剛剛解碼的PNG圖片的數據。orm

 

三、Alpha混合htm

首先說說Alpha混合,Alpha是指PNG圖片的透明度,通常咱們使用PNG圖片就是爲了能夠有半透明的顯示效果,大部分比較炫的界面,都有這些半透明特效使用,Alpha就是指定圖片的透明度,通常圖片的透明度咱們能夠分爲兩種。

第一:整張圖片的Alpha值,圖片每一個像素使用同一個Alpha值。

第二:每一個像素使用獨立的Alpha值。

對於32位的PNG圖片來講,每一個像素值,都保存了本身的Alpha值,32位PNG的像素格式:ARGB8888就是,每一個值都是一個8位值。

Alpha混合的基本公式以下:AlphaResult = ALPHA * srcPixel + ( 1 - ALPHA ) * destPixel  

下面給出一個Alpha的混合算法例子。

//支持自定義Alpha
//pBackDSSrcBmp:咱們背景DIB位圖的數據指針,也就是上面建立的DIB位圖返回的指針pBitmapData
//m_ImgDataBuf:上面解碼圖片,獲取的須要顯示的PNG圖片的數據指針
 
 
//支持自定義Alpha
void CImageData::DrawImage(HBMDC pBackDCSrcBmp, const int DstX, const int DstY, const int Alpha, DWORD DstBMPWidth)
{
    //進行Alpha混合運算    
    BYTE btAlphaSRC = 0;
    DWORD iSrcPos = 0;
    int iDstPos = 0;

    for(DWORD i=0; i<m_ImageHeigh; i++)
    {
        DWORD SrcData = (i+DstY)*DstBMPWidth + DstX;
        DWORD DstData = i*m_ImageWidth;
        for(int j=0;  j<m_ImageWidth; j++)
        {
            // 計算源圖像數據索引和像素點ALPHA
            iSrcPos = (SrcData + j) << 2;
            iDstPos = (DstData + j) << 2;    
            btAlphaSRC = m_pImgDataBuf[iDstPos+3];
            btAlphaSRC = btAlphaSRC*Alpha >> 8;

            //ALPHA混合基本公式result = ALPHA * srcPixel + ( 1 - ALPHA ) * destPixel        
            pBackDCSrcBmp[iSrcPos] += (btAlphaSRC*(m_pImgDataBuf[iDstPos]-pBackDCSrcBmp[iSrcPos]) >> 8);
            pBackDCSrcBmp[iSrcPos+1] += (btAlphaSRC*(m_pImgDataBuf[iDstPos+1]-pBackDCSrcBmp[iSrcPos+1]) >> 8);
            pBackDCSrcBmp[iSrcPos+2] += (btAlphaSRC*(m_pImgDataBuf[iDstPos+2]-pBackDCSrcBmp[iSrcPos+2]) >> 8);
        }            
    }
}
 

背景圖和須要顯示的PNG圖片,兩張圖片對應像素值,根據Alpha混合混合,就能夠獲得最終的圖片,這個就是整個顯示過程。

這裏的方法不單單針對PNG圖片,其實對於jpg和bmp也是有效的,只是這兩種圖片,通常是不包含alpha信息的,因此只能控制整張圖片的透明度,而不能控制每一個像素的透明度。

上面的運算公式通過一些處理,主要是減小運算和減小乘除法的使用,比較cpu運行乘除法比不上加減法。

下面是我本身測試的數據結果:

這個是我在一臺主頻是600M的CPU上面運行的速度對比,速度單位是毫秒,能夠對比,使用本身的Alpha混合要比微軟的AlpBlend快很多,特別是針對大圖片來講。

查過一些資料,大部分人的觀點是微軟的AlphaBlend裏面處理過多的異常處理和對圖片伸縮進行操做,致使比較慢。

有須要朋友能夠本身根據我上面說的封裝一個png貼圖類,我前面寫了一篇使用微軟ALphBlend貼圖的文章,裏面給出了完整代碼。

這個自定義的方法,代碼比較多,我就不貼出來的。

微軟AlphaBlend貼圖類:http://www.cnblogs.com/mythou/archive/2013/06/13/3133606.html

 若是有須要的朋友能夠根據本身狀況封裝不一樣的接口處理,若是有不瞭解地方,能夠留言。

 

 

新建的討論羣,有興趣能夠加入

 

VC/Wince羣:87053214 

相關文章
相關標籤/搜索