DrawDib函數組的使用
作者:羅雋
Microsoft的針對與設備無關位圖(DIB位圖),在其WIN32 SDK的Multimedia中提供了一組繪製DIB位圖的高性能函數組──DrawDib函數組。DrawDib函數組是一組不依賴於圖形設備接口(GDI)函數,而直接操作顯存的函數組。它們支持8位、16位、24位和32位圖象深度的DIB。總的來說,DrawDib函數組類似於StretchDIBits函數,它們都提供了將圖象拉伸和抖動的功能,然而,DrawDib函數組還支持圖象的解壓、數據流以及更多的顯示適配器。在某些情況下,DrawDib函數組還具有更大的優越性。但是,在某些場合下,DrawDib函數組卻不能取代StretchDIBits函數。下面就DrawDib函數組和StretchDIBits函數使用的場合加以區別和說明:
顏色信息表格式。DrawDib函數組只支持顏色信息表格式爲DIB_RGB_COLORS格式的圖象,如果要顯示以DIB_PAL_COLORS或DIB_PAL_INDICES格式的圖象,則必須用StretchDIBits函數。 光柵操作模式。DrawDib函數組只能使用SRCCOPY光柵操作模式,如果要求不僅僅使用SRCCOPY模式的話,只能用StretchDIBits函數。同樣地,如果要使用其他光柵操作,例如XOR,只能用StretchDIBits函數。 視頻及動畫回放的質量。DrawDib函數組支持數據流應用,諸如視頻片和動畫序列,它比StretchDIBits函數提供了更高的圖象質量以及對回放過程的改進。 顯示適配器。DrawDib函數組比StretchDIBits函數支持更多的顯示適配器。DrawDib函數組支持使用4位圖象深度提供16色調色板的VGA適配器,使用8位圖象深度提供256色調色板的SVGA適配器和使用16位、24位和32位圖象深度提供成千上萬種顏色的真彩色適配器。DrawDib函數組還使用了受限制的潛在能力提高了圖象在顯示適配器上的速度和質量。例如,當使用8位的顯示適配器時,DrawDib函數組有效地將真彩色圖象抖動爲256色;同樣的,使用4位的顯示適配器時,它們也將8位深度的圖象抖動成4位。 圖象拉伸。正如StretchDIBits一樣,DrawDib函數組用源矩形和目的矩形來控制一個圖象顯示的部分。可以通過改變源矩形和目的矩形的位置和大小來裁剪一幅圖象不需要的部分和拉伸某部分。如果顯示驅動不支持圖象拉伸,那麼DrawDib函數組提供了比StretchDIBits函數更有效的拉伸能力。 壓縮圖象。DrawDib函數組支持好幾種壓縮和解壓方法,其中包括遊程編碼,JPEG,Cinepak,411YUV和Indeo?。
DrawDib的操作
通過使用DrawDibOpen函數初始化DrawDib函數組。DrawDibOpen負責裝載動態連接庫(DLL),申請內存資源,DrawDib設備環境(DC),並且維持初始化相關的設備環境計數。DrawDibOpen同時返回一個其它DrawDib函數所需要使用的新的DC句柄。
當使用完DrawDib DC後,可以用DrawDibClose函數釋放它。DrawDibClose同時減少存取DLL的應用的計數。在應用程序中,DrawDibClose函數應是最後的DrawDib操作。
可以創建任意多的DrawDib DC,也可以同時使用多個DrawDib DC來繪製幾幅位圖。在應用程序中可以創建多個不同性質的DrawDib DC,這樣就可以選擇最合適的DC設置。例如,在同一應用程序中,創建兩個不同的DrawDib DC,一個用來顯示圖象的正常分辨率,另一個用來顯示圖象的放大部分。
爲了更有效地運行,DrawDib函數組需要知道顯示適配器及其驅動的信息。顯示配置信息是在第一次使用包含DrawDib函數組的DLL時,對顯示適配器通過了一系列的測試之後得到的。DrawDib函數組的所有應用都要用到這個配置信息。可以通過調用DrawDibProfileDisplay函數來強制重新進行這些測試。
通常,取得和保存顯示配置是一次性的事件。如果配置信息發現在這個系統中安裝了另一個顯示驅動時,DrawDib則重新進行測試。
圖象再現
創建了DrawDib DC後,就可以用DrawDibDraw函數將DIB繪至屏幕。當在8位深度的顯示適配器上顯示真彩色圖象時,DrawDib將自動地抖動圖象。
DrawDib也透明地支持視頻壓縮器。當顯示壓縮位圖時,可通過DrawDibGetBuffer函數得到包含了解壓圖象數據的緩衝區。如果位圖是未壓縮的,則DrawDibGetBuffer返回NULL。在應用程序中應自己區分位圖是否壓縮。
可用DrawDibUpdate宏來刷新一幅圖象的整體或一部分的顯示。
圖象序列
當DrawDibDraw函數同DrawDibBegin函數一起運用時,可以顯示相同尺寸和格式的位圖序列。DrawDib通過DrawDibBegin準備繪圖的DrawDib DC來提高DrawDibDraw的效率。如果,應用程序沒有調用DrawDibBegin,那麼DrawDibDraw會在繪圖前隱含地執行DrawDibBegin。
DrawDibBegin給DrawDibDraw提供了DrawDib的DC,DC的句柄,BITMAPINFOHEADER結構的地址和源矩形及目的矩形的尺寸。當要顯示一個位圖序列時,DrawDibDraw要檢查序列中的每幅圖象的這些值。如果DrawDibDraw檢測到這些值有任何變化,它將隱含地再次調用DrawDibBegin來調整DrawDib DC的設置。
當調用完DrawDibBegin後,就可以指定一個或多個適當的標誌來調用DrawDibDraw繪製圖象序列。只要DC句柄未改變,就可指定DDF_SAME_HDC標誌;下列參數未改變,就可指定DDF_SAME_DRAW標誌:BITMAPINFOHEADER結構的地址和源矩形及目的矩形的尺寸。
可以通過在DrawDibEnd後跟另一個DrawDibBegin調用來更新前一個DrawDibBegin設置的標誌。DrawDibEnd清除了當前的DrawDib DC的標誌和設置。後續的調用DrawDibBegin將重新初始化DrawDib DC,並重新設置適當的標誌和設置。然而,只要至少改變了以下任一個當前的標誌設置:BITMAPINFOHEADER結構的地址或是源矩形或目的矩形的尺寸,也可不使用DrawDibEnd而直接調用DrawDibBegin來更新一個DrawDib DC。
通過使用DrawDibStart和DrawDibStop函數,可以提高使用壓縮圖象的數據流操作(如回放一個視頻片)的DrawDibDraw的效率。DrawDibStart通過發送一個消息告訴視頻管理器(VCM)準備DrawDib DC來接受一個圖象流。當流結束時,DrawDibStop發送一個消息給VCM來指示它釋放申請的資源。
需要注意的是,在應用程序中必須確定源矩形和目的矩形的寬度和高度;然而卻並不需要確定這些矩形的起點。應用程序可以重新DrawDibDraw中的起點座標來使用圖象的不同部分或更新顯示的不同部分。
DrawDib函數組需要響應兩條調色板消息:WM_QUERYNEWPALETTE和WM_PALETTECHANGED。如果應用程序未注意到調色板,就需要對這些消息都增加一個各自的消息處理。
通過使用DrawDibRealize函數可在當前DC中實現當前DrawDib的調色板。應當在響應WM_QUERYNEWPALETTE和WM_PALETTECHANGED消息時,或在用DrawDibDraw函數顯示一個圖象序列的準備過程中實現調色板。
可以用DrawDibSetPallette函數用另一個調色板的映射來繪一幅圖象。DrawDibSetPallette強迫DrawDib DC使用指定的調色板,而這會影響到圖象的質量。例如,一個注意調色板的應用程序,可能已經實現了一個調色板並需要阻止DrawDib實現它自己的調色板。應用程序可以通過DrawDibSetPalette來通知DrawDib調色板的使用。
通過使用DrawDibGetPallette函數可以獲得當前前景調色板的一個句柄。如果應用程序使用了當前前景調色板,它並沒有對調色板的完全使用權,另一個應用程序能夠使這個調色板句柄無效。當使用完畢後,應用程序不應該釋放調色板,那樣會使另一個應用程序不能使用調色板。
通過使用DrawDibChangPallete函數可以爲它的調色板DrawDib來接收新的顏色值。在緊跟DrawDibChangPallete的後面的代碼裏,可以爲調色板顏色表指定新的值。當調用DrawDibChangPalette時,在DrawDib DC中未設置DDF_ANIMATE標誌的話,可以通過使用DrawDibRealize來實現調色板和DrawDibDraw重繪圖象來實現調色板的改變。如果DDF_ANIMATE標誌在DrawDib DC中設置了,就可以通過DrawDibDraw或DrawDibRealize來實現調色板和顯示着的位圖顏色的動畫。通過DrawDibEnd和DrawDibBegin可以DDF_ANIMATE標誌。
如果釋放了被選入DC的DrawDib調色板,DC使用調色板時會報告一個GDI錯誤。相反,應該使用DrawDibSetPalette改變DrawDib DC來使用省缺調色板後另一調色板。
由於以下函數會釋放DrawDib調色板,所以,除非調色板不被DC選中不應使用:DrawDibEnd,DrawDibClose和DrawDibBegin。同樣的,當使用了相同的DrawDib DC,但指定了不同的繪製參數(lpbi,dxDst,dyDst,dxSrc或dySrc)或不同格式時,DrawDibDraw也會釋放調色板。
時間計算
作爲調試應用程序的一部分,調用DrawDibTime函數可以得到一些關於完全重複特定次數DrawDib操作所需時間。DrawDibTime返回以下操作的時間:
繪製一幅位圖 解壓一幅位圖 抖動一幅位圖 拉伸一幅位圖 使用BitBlt函數變換一幅位圖 使用StrecthDIBits函數變換一幅位圖得到返回值後,DrawDibTime重新設置每項操作的計數和值。
注意,DrawDibTime只在DrawDib函數的調試版中可用。
DrawDib的使用
增加調色板消息處理
下面的例子說明了WM_PALETTECHANGED和WM_QUERYNEWPALETTE消息的處理。這個例子用了DrawDibRealize函數來進行WM_QUERYNEWPALETTE消息的處理。
應用程序應通過使目標窗口無效來讓DrawDibDraw函數重繪圖象來響應WM_QUERYNEWPALETTE消息。應用DrawDibRealize函數實現調色板來響應WM_PALETTECHANGED消息。
case WM_PALETTECHANGED:
if ((HWND) wParam == hwnd)
break;
case WM_QUERYNEWPALETTE:
hdc = GetDC(hwnd);
f = DrawDibRealize (hdd,hdc,FALSE) > 0;
ReleaseDC (hwnd,hdc);
if (f)
InvalidateRect ( hwnd,NULL,TRUE);
break;
顯示設備繪製
下面例子用DrawDibrealize函數在顯示一個位圖序列之前準備DrawDib DC.
hdc = GetDC(hwnd);
DrawDibBegin(hdd,hdc,dxDest,dyDest,lpbi,dxSrc,dySrc,NULL);
DrawDibRealize(hdd,hdc,fBackground);
DrawDibDraw(hdd,hdc,xDst,yDst,dxDst,dyDst,lpbi,lpBits,
xSrc,ySrc,dxSrc,dySrc,DDF_SAME_DRAW|DDF_SAME_HDC);
DrawDibDraw(hdd,hdc,xDst,yDst,dxDst,dyDst,lpbi,lpBits,
xSrc,ySrc,dxSrc,dySrc,DDF_SAME_DRAW|DDF_SAME_HDC);
DrawDibDraw(hdd,hdc,xDst,yDst,dxDst,dyDst,lpbi,lpBits,
xSrc,ySrc,dxSrc,dySrc,DDF_SAME_DRAW|DDF_SAME_HDC);
ReleaseDC(hwnd,hdc);
調色板動畫
下面用了DrawDibRealize,DrawDibChangPalette和DrawDibDraw函數演示調色板動畫。
能夠用DrawDibBegin函數協同DrawDibChangepalette函數改變一幅位圖的顏色。首先,在調用DrawDibBegin時指定DDF_ANIMATE標誌允許調色板改變;然後,用DrawDibChangePalette函數從調色板入口設置顏色表的值。
例如,如果lppe是一個包含新顏色的PALETTEENTRY隊列的地址,並且lpbi是在DrawDibBegin或DrawDibDraw中使用的LPBITMAPINFOHEADER結構,則後面的程序片段更新DIB的顏色表。
hdc = GetDC(hwnd);
DrawDibBegin(hdd,…,DDF_ANIMATE);
DrawDibRealize(hdd,hdc,fBackground);
DrawDibDraw(hdd,hdc,…,DDF_SAME_DRAW|DDF_SAME_HDC);
//改變顏色調用
DrawDibChangePalette(hdd,iStart,iLen,lppe);
……
ReleaseDC(hwnd,hdc);
下面給出一個實例的關鍵片段加以說明:(在Visual C++ 4.2 下Windows95或Windows NT環境下通過。)
void CTestDrawDibView::OnDraw(CDC* pDC)
{
CTestDrawDibDoc* pDoc = GetDocument();//得到文檔指針
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
m_DibMem = pDoc->m_Buf;//得到DIB的內存
if (m_DibMem == NULL)
{
//AfxMessageBox("Error in m_DibMem");
return;
}
UINT offset = pDoc->m_Off; //得到DIB數據的偏移
int xDst,yDst,dxDst,dyDst,xSrc,ySrc,dxSrc,dySrc;
LPBITMAPINFOHEADER lpbi;
LPVOID lpDibMem;
LPVOID lpbits=NULL;
// get the Windows width & height 得到窗口的寬高
RECT rect;
GetClientRect(&rect);
xDst = yDst = 0;
dxDst = rect.right - rect.left;
dyDst = rect.bottom - rect.top;
// Get Dib info得到DIB的信息
xSrc = ySrc =0;
lpDibMem = GlobalLock(m_DibMem);//鎖定內存得到指針
lpbi = (LPBITMAPINFOHEADER)lpDibMem;//得到DIB信息
dxSrc = lpbi->biWidth;
dySrc = lpbi->biHeight;
lpbits = (LPSTR)lpDibMem + offset - sizeof(BITMAPFILEHEADER);
// Draw Dib繪DIB
HDC hdc = NULL;
hdc = pDC->m_hDC;
/*
// Using SetDIBToDevice使用SetDIBToDevice函數爲對照
int line = SetDIBitsToDevice(hdc,
xDst,
yDst,
dxSrc,
dySrc,
xSrc,
ySrc,
0,
dySrc,
lpdib,//lpbits,
(LPBITMAPINFO)lpbi,
DIB_RGB_COLORS);
if(0 == line)
{
AfxMessageBox("Error in SetDIBsToDevice");
}
*/
/*
// Using StretchDIBits使用StretchDIBits函數爲對照
int line = StretchDIBits(hdc,
xDst,
yDst,
dxDst,
dyDst,
xSrc,
ySrc,
dxSrc,
dySrc,
lpbits,
(LPBITMAPINFO)lpbi,
DIB_RGB_COLORS,
SRCCOPY);
if(0 == line)
{
AfxMessageBox("Error in SetDIBsToDevice");
}
*/
// Using DrawDib使用DrawDib
// Set Dawing flag設置繪製標誌
UINT wFlags;
//標誌意義參見前面的函數參考,以下兩個標誌可繪出圖象,
//其餘標誌在這種情況下繪不出圖象。
wFlags = DDF_DONTDRAW;
//wFlags = DDF_NOTKEYFRAME;
HDRAWDIB hdd = DrawDibOpen();
if (hdd != NULL)
{
BOOL Suc = TRUE;
//具體參數請參見前面函數參考
Suc = DrawDibDraw(hdd,
hdc,
xDst,
yDst,
dxDst,
dyDst,
lpbi,
lpbits,
xSrc,
ySrc,
dxSrc,
dySrc,
wFlags);
if(Suc == FALSE) AfxMessageBox("DrawDib Failed");
/* //時間測試
DRAWDIBTIME time;
DrawDibTime(hdd ,&time);
char buf[256];
sprintf(buf,"Count %d/nDraw %d/nDecompress %d/n
Dither %d/nStretch %d/nBlt %d/n SetDIBits %d/n",
time.timeCount,time.timeDraw,
time.timeDecompress,time.timeDither,
time.timeStretch,time.timeBlt,
time.timeSetDIBits);
AfxMessageBox(buf);
*/
DrawDibClose(hdd);
}
else
AfxMessageBox("Error in DrawDibOpen");
GlobalUnlock(m_DibMem);//釋放DIB句柄
}