DrawDib函数组的使用(一)

DrawDib函数组的使用

作者:罗隽

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


  • 颜色信息表格式。DrawDib函数组只支持颜色信息表格式为DIB_RGB_COLORS格式的图象,如果要显示以DIB_PAL_COLORSDIB_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,411YUVIndeo?

 

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

    DrawDibBeginDrawDibDraw提供了DrawDibDCDC的句柄,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

    通过使用DrawDibStartDrawDibStop函数,可以提高使用压缩图象的数据流操作(如回放一个视频片)的DrawDibDraw的效率。DrawDibStart通过发送一个消息告诉视频管理器(VCM)准备DrawDib DC来接受一个图象流。当流结束时,DrawDibStop发送一个消息给VCM来指示它释放申请的资源。

    需要注意的是,在应用程序中必须确定源矩形和目的矩形的宽度和高度;然而却并不需要确定这些矩形的起点。应用程序可以重新DrawDibDraw中的起点坐标来使用图象的不同部分或更新显示的不同部分。

 

调色板

    DrawDib函数组需要响应两条调色板消息:WM_QUERYNEWPALETTEWM_PALETTECHANGED。如果应用程序未注意到调色板,就需要对这些消息都增加一个各自的消息处理。

    通过使用DrawDibRealize函数可在当前DC中实现当前DrawDib的调色板。应当在响应WM_QUERYNEWPALETTEWM_PALETTECHANGED消息时,或在用DrawDibDraw函数显示一个图象序列的准备过程中实现调色板。

    可以用DrawDibSetPallette函数用另一个调色板的映射来绘一幅图象。DrawDibSetPallette强迫DrawDib DC使用指定的调色板,而这会影响到图象的质量。例如,一个注意调色板的应用程序,可能已经实现了一个调色板并需要阻止DrawDib实现它自己的调色板。应用程序可以通过DrawDibSetPalette来通知DrawDib调色板的使用。

    通过使用DrawDibGetPallette函数可以获得当前前景调色板的一个句柄。如果应用程序使用了当前前景调色板,它并没有对调色板的完全使用权,另一个应用程序能够使这个调色板句柄无效。当使用完毕后,应用程序不应该释放调色板,那样会使另一个应用程序不能使用调色板。

    通过使用DrawDibChangPallete函数可以为它的调色板DrawDib来接收新的颜色值。在紧跟DrawDibChangPallete的后面的代码里,可以为调色板颜色表指定新的值。当调用DrawDibChangPalette时,在DrawDib DC中未设置DDF_ANIMATE标志的话,可以通过使用DrawDibRealize来实现调色板和DrawDibDraw重绘图象来实现调色板的改变。如果DDF_ANIMATE标志在DrawDib DC中设置了,就可以通过DrawDibDrawDrawDibRealize来实现调色板和显示着的位图颜色的动画。通过DrawDibEndDrawDibBegin可以DDF_ANIMATE标志。

    如果释放了被选入DCDrawDib调色板,DC使用调色板时会报告一个GDI错误。相反,应该使用DrawDibSetPalette改变DrawDib DC来使用省缺调色板后另一调色板。

    由于以下函数会释放DrawDib调色板,所以,除非调色板不被DC选中不应使用:DrawDibEnd,DrawDibCloseDrawDibBegin。同样的,当使用了相同的DrawDib DC,但指定了不同的绘制参数(lpbi,dxDst,dyDst,dxSrcdySrc)或不同格式时,DrawDibDraw也会释放调色板。

 

时间计算

    作为调试应用程序的一部分,调用DrawDibTime函数可以得到一些关于完全重复特定次数DrawDib操作所需时间。DrawDibTime返回以下操作的时间:

  • 绘制一幅位图
  • 解压一幅位图
  • 抖动一幅位图
  • 拉伸一幅位图

  • 使用BitBlt函数变换一幅位图

  • 使用StrecthDIBits函数变换一幅位图

    得到返回值后,DrawDibTime重新设置每项操作的计数和值。

    注意,DrawDibTime只在DrawDib函数的调试版中可用。

 

DrawDib的使用

    增加调色板消息处理

    下面的例子说明了WM_PALETTECHANGEDWM_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,DrawDibChangPaletteDrawDibDraw函数演示调色板动画。

    能够用DrawDibBegin函数协同DrawDibChangepalette函数改变一幅位图的颜色。首先,在调用DrawDibBegin时指定DDF_ANIMATE标志允许调色板改变;然后,用DrawDibChangePalette函数从调色板入口设置颜色表的值。

    例如,如果lppe是一个包含新颜色的PALETTEENTRY队列的地址,并且lpbi是在DrawDibBeginDrawDibDraw中使用的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 Windows95Windows 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 DibDIB

            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句柄

       }

posted on 2004-10-23 16:07  阿彪  阅读(2497)  评论(0编辑  收藏  举报

导航