GDI+ 雙緩衝實現和區域性刷新技術
前言:GDI+很好用,但要將所有影象混合,必須要用到UpdateLayeredWindow,但UpdateLayeredWindow註定每次重新整理都要重新整理整個窗體,也就是說,哪怕我們只是改變的只是一個畫素,也都要重新繪製整個窗體,就沒有像GDI中那樣有區域裁剪的功能,真無語了,搜遍整個網路也沒找到UpdateLayeredWindow的區域性重新整理方案,最後想到在MFC中是可以使用GDI+的,而MFC中的重新整理方案就是區域性重新整理的,這說明,利用GDI的函式bitblt或alphaBlend是可以整合GDI+的,最後嘗試了一下,成功了,下面分享給大家。
本文摘要:這個工程是在第一篇《WIN32介面開發之一:初試載入背景介面》的基礎上講解的,所有請大家先看看第一篇,這篇內容實現的效果是,建立一個背景圖案,在圖案上模擬一個按鈕,按鈕原是紅色塊,當點選按鈕的時候,變成綠色色塊,滑鼠彈起時,還原為紅色色塊。
正常狀態:
佔擊狀態:
思想:首先,在第一次建立工程時,我們在在相容DC繪圖,並把這個DC儲存起來,當下次重繪時,重新建立一個相容DC,用AlphaBlend將我們儲存的原相容DC上的內容複製到當前的相容DC上,然後再在要更新的區域重新畫圖就是了,這裡我們只需要更新按鈕的區域。
一、幾個全域性變數
- HDC hdcBKMemory=NULL;//相容DC
- HBITMAP hBKBitmap=NULL;//相容DC上的畫布
- HGDIOBJ hBKBitmapOld=NULL;//被選出的原相容DC上的預設畫布
我們先看建立的程式碼:
- HDC hDC = ::GetDC(m_hWnd);
- if (hdcBKMemory==NULL)
- {
- hdcBKMemory = CreateCompatibleDC(hDC);
- //建立背景畫布
- BITMAPINFOHEADER stBmpInfoHeader = { 0 };
- int nBytesPerLine = ((sizeWindow.cx * 32 + 31) & (~31)) >> 3;
- stBmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
- stBmpInfoHeader.biWidth = sizeWindow.cx;
- stBmpInfoHeader.biHeight = sizeWindow.cy;
- stBmpInfoHeader.biPlanes = 1;
- stBmpInfoHeader.biBitCount = 32;
- stBmpInfoHeader.biCompression = BI_RGB;
- stBmpInfoHeader.biClrUsed = 0;
- stBmpInfoHeader.biSizeImage = nBytesPerLine * sizeWindow.cy;
- PVOID pvBits = NULL;
- hBKBitmap = ::CreateDIBSection(NULL, (PBITMAPINFO)&stBmpInfoHeader, DIB_RGB_COLORS, &pvBits, NULL, 0);
- assert(hBKBitmap != NULL);
- hBKBitmapOld = ::SelectObject( hdcBKMemory, hBKBitmap);
- //gdi+畫圖
- Gdiplus::Graphics graph(hdcBKMemory);
- graph.SetSmoothingMode(Gdiplus::SmoothingModeNone);
- graph.DrawImage(pImage, 0, 0, sizeWindow.cx, sizeWindow.cy);
- graph.FillRectangle(&SolidBrush(Color::Green),10,10,25,25);
- graph.FillRectangle(&SolidBrush(Color::Red),100,50,30,30);//模擬按鈕
- graph.ReleaseHDC(hdcBKMemory);
- }
再看釋放:
因為我們要一直用它,直到銷燬,所以我們在WM_DESTROY訊息中銷燬這些變數:
- case WM_DESTROY:
- PostQuitMessage(100);
- delete pImage;
- ::SelectObject( hdcBKMemory, hBKBitmapOld); //不要把預設的點陣圖選回來,如果選回來的話,我們新建的點陣圖就被替換掉了,當然我們上面畫的東東也就沒有了
- ::DeleteObject(hBKBitmapOld);
- ::DeleteObject(hBKBitmap);
- ::DeleteDC(hdcBKMemory);
- break;
先看程式碼:
- HDC hdcEnd = CreateCompatibleDC(hDC);//新建相容DC
- BITMAPINFOHEADER stBmpInfoHeader2 = { 0 };
- int nBytesPerLine2 = ((sizeWindow.cx * 32 + 31) & (~31)) >> 3;
- stBmpInfoHeader2.biSize = sizeof(BITMAPINFOHEADER);
- stBmpInfoHeader2.biWidth = sizeWindow.cx;
- stBmpInfoHeader2.biHeight = sizeWindow.cy;
- stBmpInfoHeader2.biPlanes = 1;
- stBmpInfoHeader2.biBitCount = 32;
- stBmpInfoHeader2.biCompression = BI_RGB;
- stBmpInfoHeader2.biClrUsed = 0;
- stBmpInfoHeader2.biSizeImage = nBytesPerLine2 * sizeWindow.cy;
- PVOID pvBits2 = NULL;
- HBITMAP hbmpMem2 = ::CreateDIBSection(NULL, (PBITMAPINFO)&stBmpInfoHeader2, DIB_RGB_COLORS, &pvBits2, NULL, 0);//新建畫布
- HGDIOBJ hEndBitmapOld=SelectObject(hdcEnd,hbmpMem2);
- POINT ptSrc = { 0, 0};
- BLENDFUNCTION blendFunc;
- blendFunc.BlendOp = 0;
- blendFunc.BlendFlags = 0;
- blendFunc.AlphaFormat = 1;
- blendFunc.SourceConstantAlpha = 255;//AC_SRC_ALPHA
- ::AlphaBlend(hdcEnd,0,0,sizeWindow.cx,sizeWindow.cy,hdcBKMemory,0,0,sizeWindow.cx,sizeWindow.cy,blendFunc);//將背景複製到新畫布上
- Graphics graph2(hdcEnd);
- if (inBtnRect)//這個變數下面講解
- {
- graph2.FillRectangle(&SolidBrush(Color::Green),100,50,30,30);//按鈕按下狀態
- }else{
- graph2.FillRectangle(&SolidBrush(Color::Red),100,50,30,30);//按鈕正常狀態
- }
- POINT ptWinPos = { rcWindow.left, rcWindow.top };
- //UpdateLayeredWindow
- HMODULE hFuncInst = LoadLibrary(_T("User32.DLL"));
- typedefBOOL (WINAPI *MYFUNC)(HWND, HDC, POINT*, SIZE*, HDC, POINT*, COLORREF, BLENDFUNCTION*, DWORD);
- MYFUNC UpdateLayeredWindow;
- UpdateLayeredWindow = (MYFUNC)::GetProcAddress(hFuncInst, "UpdateLayeredWindow");
- //不會發送 WM_SIZE和WM_MOVE訊息
- if(!UpdateLayeredWindow(m_hWnd, hDC, &ptWinPos, &sizeWindow, hdcEnd, &ptSrc, 0, &blendFunc, ULW_ALPHA))
- {
- assert(L"UpdateLayeredWindow 呼叫失敗");
- TCHAR tmp[255] = {_T('\0')};
- }//使用UpdateLayeredWindow更新到當前窗體上
- //釋放資源
- graph2.ReleaseHDC(hdcEnd);
- SelectObject(hdcEnd,hEndBitmapOld);
- ::DeleteObject(hFuncInst);
- ::DeleteObject(hEndBitmapOld);
- ::DeleteObject(hbmpMem2);
- ::DeleteDC(hdcEnd);
- ::DeleteDC(hDC);
講解:這段程式碼看起來比較長,但主要是這麼個流程:
1、建立新的相容DC
2、建立畫布並將此畫布選到當前的相容DC中,準備做畫
3、應用AlphaBlend將原背景複製到當前畫布中,這時當前的相容DC就具有了背景圖案
4、在原背景圖案上畫上按鈕
5、最後利用UpdateLayeredWindow將最終的圖案複製到當前的窗體上
執行後的軟體圖案是這樣的:
4、新增按鈕及響應
1、添加當前按鈕狀態的變數(全域性變數)
- bool inBtnRect=false;//儲存當前按鈕是否是被按下,FALSE是沒被按下,TRUE是按下
- case WM_LBUTTONDOWN:
- {
- POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
- RectF btnRectf(100,50,30,30);
- if (btnRectf.Contains(pt.x,pt.y))
- {
- inBtnRect=true;
- SendMessage(hwnd,WM_PAINT,NULL,NULL);
- SetCapture(hwnd);
- }
- }
- break;
1、首先用GET_X_LPARAM和GET_Y_LPARAM獲取當前滑鼠在窗體中的點選位置;
2、如果點選位置在按鈕區域內的話,就重新整理窗體,並且設定焦點
3、在WM_LBUTTONUP新增程式碼
- case WM_LBUTTONUP:
- {
- if (inBtnRect)
- {
- ReleaseCapture();
- inBtnRect=false;
- SendMessage(hwnd,WM_PAINT,NULL,NULL);
- }
- }
- break;
宣告:軟體所用圖片來於網路,感謝金山影音漂亮的介面圖片