三、与设备有关的位图(DDB) 与设备有关的位图,相对与与设备无关位图来讲,比较简单。只所以简单是因为我们根本没有必要去学习它的结构。因为,与设备有关的位图的格式总是与其设备场景(顺便说一下,很多书把设备场景叫做“设备上下文”)有关。但2色位图除外。如果你正坐在计算机前,不要认为计算机中只存在一种与设备有关的位图,其实不然。所谓设备并不是指一台计算机,内存,显示缓存,打印机,等都有各自的位图数据格式。不要对与设备有关的位图进行对其结构的任何猜测。你见过我吗?没有吧?但你是否觉得与我交往仍然是很有效呢?我们可以通过Email,因特网进行交流。你就把我理解为一个人,一个远方的朋友就可以了,是不是?对与设备有关的位图也就这样想就可以了。它的确是存在着的,就在计算机的某个设备内部(比如内存,打印机)。当然我们不能通过Email跟它交往,而是要靠函数,范围包括创建,获取信息,设置信息,销毁等等。用惯了你会发现,这也很得劲儿,其难度也就相当与给我发一个E-Mail── 填写需要的地址,需要的标题和内容,然后发送。当然,这里要做的是为一个函数的调用为其添写需要的参数。如果你真的想见它一面,我倒有个方法,是家传秘方呦!好好听,你可以拿锤子把你的计算机用力,请记住一定要用力,用力砸一下,然后打开机箱看看它出来了没有,一般成功率能达到99%。但由于存在那1%的失败的可能,我得先声明如果见不到后果自负∶) 使用与设备无关位图的过程当中始终不能忘记的一点是,确保位图与设备的兼容性。你不能把一个内存设备场景中的位图直接选入到打印机设备场景,会出现异常或错误。这里所说的设备其实就是指设备场景。为了创建一个与指定设备场景兼容的设备相关位图,可以使用CreateBitmap或者CreateCompatibleBitmap。但两个函数的功能是不同的。我的经验是,在创建一个Mask(掩模位图)图象的时候使用CreateBitmap,而创建一个彩色位图的时候使用CreateCompatibleBitmap。还有几个函数,如CreateDIBitmap,它可以根据一个DIB(设备无关位图)的基础上创建一个与设备相关位图。反正,你应该注意到,几乎所有的与设备相关位图有关的API函数都具有一个hDC参数,有时候看起来没有必要的,但应当清楚这主要是为了保证设备的兼容性而给出的。说明与设备有关位图的有些信息是记录在设备场景中的。 作为GDI对象,设备相关位图被创建后,需要把它选入到设备场景,这和向设备场景选入一个画笔完全一样,可以用SelectObject函数,只是在先前要写画笔句柄的参数位置上写下位图句柄即可。当你不再使用一个被你创建的位图的时候,应当用DeleteObject函数删除之,以释放系统资源。为了删除它,你还需要用SelectObject函数把原来的位图选回到设备场景,这样你为设备场景选入过的位图就退了回来,处于可以删除或重新被选入的状态。这需要你在为设备场景选入一个你自己创建的位图时保存旧的(原先的)位图句柄。反正这和操纵画笔或画刷之类的GDI对象完全一样。同样,不可将一个位图同时选入两个不同的设备场景。 创建和使用位图的常用技巧是,创建一个DC或多个DC以及相应的位图,然后在后台(用户看不到)进行各种光栅运算和加工处理后,最后把形成的图象一次性发送到关联设备场景中(关联设备场景中的图象会自动影射到视频内存,如PictureBox和hDC所代表的内存。详细情况请参考前期教程设备场景部分)中(常用BitBlt),以获取速度和最大限度地避免屏幕闪烁(后面给出了例子)。这主要是因为向屏幕输出图象的函数执行速度比较慢,而且图象的加工过程对用户来说没有必要看到的。为了创建若干个设备场景,你需要多次用CreateCompatibleDc函数(这也是最简单的方法),它可以使你获得一个与指定设备场景兼容的设备场景。反正你应该保证创建位图中所使用的hDC参数和创建设备场景中所使用的hDC参数保持一致。别忘了最后用DeleteDC来删除你自己创建的设备场景。在删除设备场景前,应当从该设备场景中抽出为其选入的位图,并将其销毁。以上这种技巧在一本VC书上叫做“双缓冲”,意识是把操作分成前台和后台两部分同时进行。 接下来给出与设备有关位图的函数吧,请您不要客气随便看。 与设备有关位图函数 函 数 说 明
CreateBitmap 创建一幅位图,并选择性地初始化位图数据
CreateBitmapIndirect 在一个BITMAP数据结构的基础上创建一幅位图
CreateCompatibleBitmap 创建与指定设备场景兼容的一幅位图
GetBitmapBits 获取位图的像素位数据
GetBitmapDimensionEx 取得一幅位图的大小
LoadBitmap 从资源文件中载入一幅固有的系统位图
LoadImage 一个常规用途的函数,用于装载图象、图标及指针
SetBitmapBits 根据一个数据缓冲区设置位图图象。使用GetDIBits吧,更好用。
SetBitmapDimensionEx 设置位图的大小。 位图传输函数 函 数 说 明
BitBlt 位块传输。将一个图象区域传到另一个图象区域。必须掌握到熟背。
PatBlt 图案块传输。根据指定图案(就象由一个刷子表示的那样)填充一个图象区域。
GetStretchBltMode 进行伸缩处理时,用于判断Windows删除线段或像素方式。
MaskBlt 执行复杂的图象传输,同时进行掩模(MASK)处理
PlgBlt 平行四边形块传输。允许我们伸缩、扭曲及放置一幅图象。这个函数我在Windows95中用过,可始终没有成功。功能看起来很不错。
SetStretchBltMode 进行伸缩处理时,用于决定Windows删除线段或像素的方式。
StretchBlt 将一幅位图从一个设备场景复制到另一个。源和目标DC相互间必须兼容。这个函数会在设备场景中定义一个目标矩形,并在位图中定义一个源图象。源矩形会根据需要进行伸缩,以便与目标矩形的大小相符。
DIB并不存在于设备场景中(device context)。在它被选入设备场景或在它被画入设备场景前,DIB必须被翻译为DDB。你可以用StretchDIBits函数完成这一工作。本教程提供了一个源代码例程。通过它你能够学习DDB到DIB,再从DIB到DDB的转化过程。程序名为vbplay46.vbp。实际上是源码解析中的演示程序第46号。
为了使用好位块传输,你需要掌握图形光栅方面的知识。其内容和上一期给出的位操作运算在其原理上相同。只是需要进行的并不是一个、两个或四个直接等的数据,而是一个位图数据。位图数据也是以字节来构成的,只是长度要长得多。为了使用光栅运算,你需要有一个自己的表格,以便从中查找并计划光栅方案。以下是Dan的书中的,也是我最喜欢的一个表(详细的中文说明格式的表不如它好用)∶ SRCCOPY Destination = Source SRCPAINT Destination = Source OR Destination SRCAND Destination = Source AND Destination SRCINVERT Destination = Source XOR Destination SRCERASE Destination = Source AND (NOT Destination) NOTSRCCOPY Destination = NOT Source NOTSRCERASE Destination = (NOT Source) AND (NOT Destination) MERGECOPY Destination = Source AND Pattern MERGEPAINT Destination = (NOT Source) OR Destination PATCOPY Destination = Pattern PATPAINT Destination = (NOT Source) OR Pattern OR Destination PATINVERT Destination = Pattern XOR Destination DSTINVERT Destination = NOT Destination BLACKNESS Destination = 0 WHITENESS Destination = All bits set to 1 可惜,少一个很重要的。可别担心,本老师有办法∶ &H220326 Destination=(NOT Source) AND Detination 注∶以上,Destination指的是目标位图(BitBlt中前一个DC中的),Source是来源位图(BitBlt中的后一个DC中的)。 以下给出一个例子,是我常用的一个函数。它用来实现透明复制位图。你可以在你的程序中直接粘贴使用。它总结了本节给出的内容。 作者(xing) 1999年10月17日 整理备用 功能∶ 透明复制位图 参数表∶ hDestDC -------- Long,目标设备场景 x,y ------------ Long,对目标DC中目标矩形左上角位置进行描述的那个点。用目标DC的逻辑坐标表示 nWidth,nHeight - Long,欲传输图象的宽度和高度 hSrcDC --------- Long,源设备场景。如光栅运算未指定源,则应设为0 xSrc,ySrc ------ Long,对源DC中源矩形左上角位置进行描述的那个点。用源DC的逻辑坐标表示 TransColor ----- OLE_COLOR,被透明处理的颜色 Sub TransBlt(ByVal hDestDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal TransColor As OLE_COLOR) Dim dl As Long Dim OrigColor As Long Dim OrigMode As Long Dim saveDC As Long Dim maskDC As Long Dim invDC As Long Dim resultDC As Long Dim hSaveBmp As Long Dim hMaskBmp As Long Dim hInvBmp As Long Dim hResultBmp As Long Dim hSavePrevBmp As Long Dim hMaskPrevBmp As Long Dim hInvPrevBmp As Long Dim hDestPrevBmp As Long saveDC = CreateCompatibleDC(hDestDC) maskDC = CreateCompatibleDC(hDestDC) invDC = CreateCompatibleDC(hDestDC) resultDC = CreateCompatibleDC(hDestDC) '按照规定的格式创建一幅与设备有关位图 hMaskBmp = CreateBitmap(nWidth, nHeight, 1, 1, ByVal 0&) hInvBmp = CreateBitmap(nWidth, nHeight, 1, 1, ByVal 0&) '创建一幅与设备有关位图 hResultBmp = CreateCompatibleBitmap(hDestDC, nWidth, nHeight) hSaveBmp = CreateCompatibleBitmap(hDestDC, nWidth, nHeight) hSavePrevBmp = SelectObject(saveDC, hSaveBmp) hMaskPrevBmp = SelectObject(maskDC, hMaskBmp) hInvPrevBmp = SelectObject(invDC, hInvBmp) hDestPrevBmp = SelectObject(resultDC, hResultBmp) '产生Mask图象 OrigColor = SetBkColor(hSrcDC, TransColor) dl& = BitBlt(maskDC, 0, 0, nWidth, nHeight, hSrcDC, xSrc, ySrc, vbSrcCopy) TransColor = SetBkColor(hSrcDC, OrigColor) 'invDC的图象将与maskDC图象相反 dl& = BitBlt(invDC, 0, 0, nWidth, nHeight, maskDC, 0, 0, vbNotSrcCopy) 'resultDC的图象将成为被写位置的图象 dl& = BitBlt(resultDC, 0, 0, nWidth, nHeight, hDestDC, x, y, vbSrcCopy) 'resultDC中,需要新写的位置将变为黑色 dl& = BitBlt(resultDC, 0, 0, nWidth, nHeight, maskDC, 0, 0, vbSrcAnd) dl& = BitBlt(saveDC, 0, 0, nWidth, nHeight, hSrcDC, xSrc, ySrc, vbSrcCopy) 'resultDC中不被写入的颜色成为黑色 dl& = BitBlt(saveDC, 0, 0, nWidth, nHeight, invDC, 0, 0, vbSrcAnd) '将两幅图合并起来 dl& = BitBlt(resultDC, 0, 0, nWidth, nHeight, saveDC, 0, 0, vbSrcInvert) '完工后输出 dl& = BitBlt(hDestDC, x, y, nWidth, nHeight, resultDC, 0, 0, vbSrcCopy) SelectObject saveDC, hSavePrevBmp SelectObject resultDC, hDestPrevBmp SelectObject maskDC, hMaskPrevBmp SelectObject invDC, hInvPrevBmp DeleteObject hSaveBmp DeleteObject hMaskBmp DeleteObject hInvBmp DeleteObject hResultBmp DeleteDC saveDC DeleteDC maskDC DeleteDC invDC DeleteDC resultDC End Sub |