DrawDib函数组的使用
作者:罗隽
Microsoft的针对与设备无关位图(DIB位图),在其WIN32 SDK的Multimedia中提供了一组绘制DIB位图的高性能函数组──DrawDib函数组。DrawDib函数组是一组不依赖于图形设备接口(GDI)函数,而直接操作显存的函数组。它们支持8位、16位、24位和32位图象深度的DIB。总的来说,DrawDib函数组类似于StretchDIBits函数,它们都提供了将图象拉伸和抖动的功能,然而,DrawDib函数组还支持图象的解压、数据流以及更多的显示适配器。在某些情况下,DrawDib函数组还具有更大的优越性。但是,在某些场合下,DrawDib函数组却不能取代StretchDIBits函数。下面就DrawDib函数组和StretchDIBits函数使用的场合加以区别和说明:
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返回以下操作的时间:
得到返回值后,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句柄
}