⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 14. 位图和bitblt.txt

📁 本书介绍了在Microsoft Windows 98、Microsoft Windows NT 4.0和Windows NT 5.0下程序写作的方法
💻 TXT
📖 第 1 页 / 共 5 页
字号:

图案(P):1 1 0 0

目的(D):1 0 1 0
 布尔操作
 ROP代码
 名称
 
结果:
 0 0 0 0
 0
 0x000042
 BLACKNESS
 
 0 0 0 1
 ~(P | D)
 0x0500A9
  
 0 0 1 0
 ~P & D
 0x0A0329
  
 0 0 1 1
 ~P
 0x0F0001
  
 0 1 0 0
 P & ~D
 0x500325
  
 0 1 0 1
 ~D
 0x550009
 DSTINVERT
 
 0 1 1 0
 P ^ D
 0x5A0049
 PATINVERT
 
 0 1 1 1
 ~(P & D)
 0x5F00E9
  
 1 0 0 0
 P & D
 0xA000C9
  
 1 0 0 1
 ~(P ^ D)
 0xA50065
  
 1 0 1 0
 D
 0xAA0029
  
 1 0 1 1
 ~P | D
 0xAF0229
  
 1 1 0 0
 P
 0xF00021
 PATCOPY
 
 1 1 0 1
 P | ~D
 0xF50225
  
 1 1 1 0
 P | D
 0xFA0089
  
 1 1 1 1
 1
 0xFF0062
 WHITENESS
 

下面列出了PatBlt一些更常见用途。如果想画一个黑色矩形,您可呼叫

PatBlt (hdc, x, y, cx, cy, BLACKNESS) ;
        
要画一个白色矩形,请用

PatBlt (hdc, x, y, cx, cy, WHITENESS) ;
        
函数

PatBlt (hdc, x, y, cx, cy, DSTINVERT) ;
        
用于改变矩形的颜色。如果目前设备内容中选择了WHITE_BRUSH,那么函数

PatBlt (hdc, x, y, cx, cy, PATINVERT) ;
        
也改变矩形。

您可以再次呼叫FillRect函数来用画笔充满一个矩形区域:

FillRect (hdc, &rect, hBrush) ;
        
FillRect函数相同于下列代码:

hBrush = SelectObject (hdc, hBrush) ;
        
PatBlt (hdc,       rect.left, rect.top,
        
                                         rect.right - rect.left,
        
                                         rect.bottom - rect.top, PATCOPY) ;
        
SelectObject (hdc, hBrush) ;
        
实际上,此程序代码是Windows用于执行FillRect函数的动作。如果您呼叫

InvertRect (hdc, &rect) ;
        
Windows将其转换成函数:

PatBlt (hdc,       rect.left, rect.top,
        
                                         rect.right - rect.left,
        
                                         rect.bottom - rect.top, DSTINVERT) ;
        
在介绍PatBlt函数的语法时,我说过点(x,y)指出了矩形的左上角,而且此矩形宽度为cx单位,高度为cy单位。此叙述并不完全正确。BitBlt、PatBlt和StretchBlt是最合适的GDI画图函数,它们根据从一个角测得的逻辑宽度和高度来指定逻辑直角坐标。矩形边框用到的其它所有GDI画图函数都要求根据左上角和右下角的坐标来指定坐标。对于MM_TEXT映像模式,上面讲述的PatBlt参数就是正确的。然而对于公制的映像模式来说,就不正确。如果您使用一的cx和cy值,那么点(x,y)将是矩形的左下角。如果希望点(x,y)是矩形的左上角,那么cy参数必须设为矩形的负高度。

如果想更精确,用PatBlt修改颜色的矩形将通过cx的绝对值获得逻辑宽度,通过cy的绝对值获得逻辑高度。这两个参数可以是负值。由逻辑点(x, y)和(x + cx, y + cy)给定的两个角定义了矩形。矩形的左上角通常属于PatBlt修改的区域。右上角则超出了矩形的范围。根据映像模式和cx、cy参数的符号,矩形左上角的点应为(x, y)、(x, y + cy)、(x + cx, y)或者(x + cx, y + cy)。

如果给MM_LOENGLISH设定了映像模式,并且您想在显示区域左上角的一小块正方形上使用PatBlt,您可以使用

PatBlt (hdc, 0, 0, 100, -100, dwROP) ;
        

PatBlt (hdc, 0, -100, 100, 100, dwROP) ;
        

PatBlt (hdc, 100, 0, -100, -100, dwROP) ;
        

PatBlt (hdc, 100, -100, -100, 100, dwROP) ;
        
给PatBlt设定正确参数最容易的方法是将x和y设为矩形左上角。如果映像模式定义y坐标随着向上卷动显示而增加,那么请使用负的cy参数。如果映像模式定义x坐标向左增加(很少有人用),则需要使用负的cx参数。

GDI 位图对象


我在本章前面已提到过Windows从1.0开始就支持GDI位图对象。因为在Windows 3.0发表了设备无关位图,GDI位图对象有时也称为设备相关位图,或者DDB。我尽量不全部引用device-dependent bitmap的全文,因为它看上去与device-independent bitmap类似。缩写DDB会好一些,因为我们很容易把它与DIB区别开来。

对程序写作者来说,现存的两种不同型态的位图从Windows 3.0开始就更为混乱。许多有经验的Windows程序写作者都不能准确地理解DIB和DDB之间的关系。(恐怕本书的Windows 3.0版本不能澄清这个问题)。诚然,DIB和DDB在许多方面是相关的:DIB与DDB能相互转换(尽管转换程序中会丢失一些信息)。然而DIB和DDB是不可以相互替换的,并且不能简单地选择一种方法来表示同一个可视数据。

如果我们能假设说DIB一定会替代DDB,那以后就会很方便了。但现实并不是如此,DDB还在Windows中扮演着很重要角色,尤其是您在乎程序执行表现好坏时。

建立DDB


DDB是Windows图形设备接口的图形对象之一(其中还包括绘图笔、画刷、字体、metafile和调色盘)。这些图形对象储存在GDI模块内部,由应用程序软件以句柄数字的方式引用。您可以将DDB句柄储存在一个HBITMAP(「handle to a bitmap:位图句柄」)型态的变量中,例如:

HBITMAP hBitmap ;
        
然后通过呼叫DDB建立的一个函数来获得句柄,例如:CreateBitmap。这些函数配置并初始化GDI内存中的一些内存来储存关于位图的信息,以及实际位图位的信息。应用程序不能直接存取这段内存。位图与设备内容无关。当程序使用完位图以后,就要清除这段内存:

DeleteObject (hBitmap) ;
        
如果程序执行时您使用了DDB,那么程序终止时,您可以完成上面的操作。

CreateBitmap函数用法如下:

hBitmap = CreateBitmap (cx, cy, cPlanes, cBitsPixel, bits) ;
        
前两个参数是位图的宽度和高度(以图素为单位),第三个参数是颜色面的数目,第四个参数是每图素的位数,第五个参数是指向一个以特定颜色格式存放的位数组的指针,数组内存放有用来初始化该DDB的图像。如果您不想用一张现有的图像来初始化DDB,可以将最后一个参数设为NULL。以后您还是可以设定该DDB内图素的内容。

使用此函数时,Windows也允许建立您喜欢的特定型态GDI位图对象。例如,假设您希望位图宽7个图素、高9个图素、5个?色位面,并且每个图素占3位,您只需要执行下面的操作:

hBitmap = CreateBitmap (7, 9, 5, 3, NULL) ;
        
这时Windows会好好给您一个有效的位图句柄。

在此函数呼叫期间,Windows将储存您传递给函数的信息,并为图素位配置内存。粗略的计算是此位图需要7×9×5×3,即945位,这需要比118个字节还多几个位。

然而,Windows为位图配置好内存以后,每行图素都占用许多连贯的字节,这样

iWidthBytes = 2 * ((cx * cBitsPixel + 15) / 16) ;
        
或者C程序写作者更倾向于写成:

iWidthBytes = (cx * cBitsPixel + 15) & ~15) >> 3 ;
        
因此,为DDB配置的内存就是:

iBitmapBytes = cy * cPlanes * iWidthBytes ;
        
本例中,iWidthBytes占4字节,iBitmapBytes占180字节。

现在,知道一张位图有5个颜色位面,每图素占3个颜色位有什么意义吗?真是见鬼了,这甚至不能把它称作一个习题作业。虽然您让GDI内部配置了些内存,并且让这些内存有一定结构的内容,但是您这张位图完全作不出任何有用的事情来。

实际上,您将用两种型态的参数来呼叫CreateBitmap。

cPlanes和cBitsPixel都等于1(表示单色位图);或者
  
cPlanes和cBitsPixel都等于某个特定设备内容的值,您可以使用PLANES和BITSPIXEL索引来从GetDeviceCaps函数获得。
  
更现实的情况下,您只会在第一种情况下呼叫CreateBitmap。对于第二种情况,您可以用CreateCompatibleBitmap来简化问题:

hBitmap = CreateCompatibleBitmap (hdc, cx, cy) ;
        
此函数建立了一个与设备兼容的位图,此设备的设备内容句柄由第一个参数给出。CreateCompatibleBitmap用设备内容句柄来获得GetDeviceCaps信息,然后将此信息传递给CreateBitmap。除了与实际的设备内容有相同的内存组织之外,DDB与设备内容没有其它联系。

CreateDiscardableBitmap函数与CreateCompatibleBitmap的参数相同,并且功能上相同。在早期的Windows版本中,CreateDiscardableBitmap建立的位图可以在内存减少时由Windows将其从内存中清除,然后程序再重建位图数据。

第三个位图建立函数是CreateBitmapIndirect:

hBitmap CreateBitmapIndirect (&bitmap) ;
        
其中bitmap是BITMAP型态的结构。BITMAP结构定义如下:

typedef struct _tagBITMAP
        
{
        
           LONG          bmType ;                      // set to 0
        
           LONG          bmWidth ;                     // width in pixels
        
           LONG          bmHeight ;                   // height in pixels
        
           LONG          bmWidthBytes ;                // width of row in bytes
        
           WORD          bmPlanes ;                    // number of color planes
        
           WORD         bmBitsPixel ;                 // number of bits per pixel
        
           LPVOIDbmBits ;                             // pointer to pixel bits
        
}
        
BITMAP, * PBITMAP ;
        
在呼叫CreateBitmapIndirect函数时,您不需要设定bmWidthBytes字段。Windows将为您计算,您也可以将bmBits字段设定为NULL,或者设定为初始化位图时用的图素位地址。

GetObject函数内也使用BITMAP结构,首先定义一个BITMAP型态的结构。

BITMAP bitmap ;
        
并呼叫函数如下:

GetObject (hBitmap, sizeof (BITMAP), &bitmap) ;
        
Windows将用位图信息填充BITMAP结构的字段,不过,bmBits字段等于NULL。

您最后应呼叫DeleteObject来清除程序内建立的所有位图。

位图位


用CreateBitmap或CreateBitmapIndirect来建立设备相关GDI位图对象时,您可以给位图图素位指定一个指针。或者您也可以让位图维持未初始化的状态。在建立位图以后,Windows还提供两个函数来获得并设定图素位。

要设定图素位,请呼叫:

SetBitmapBits (hBitmap, cBytes, &bits) ;
        
GetBitmapBits函数有相同的语法:

GetBitmapBits (hBitmap, cBytes, &bits) ;
        
在这两个函数中,cBytes指明要复制的字节数,bits是最少cBytes大小的缓冲区。

DDB中的图素位从顶列开始排列。我在前面说过,每列的字节数都是偶数。除此之外,没什么好说明的了。如果位图是单色的,也就是说它有1个位面并且每个图素占1位,则每个图素不是1就是0。每列最左边的图素是本列第一个字节最高位的位。我们在本章的后面讲完如何显示单色DDB之后,将做一个单色的DDB。

对于非单色位图,应避免出现您需要知道图素位含义的状况。例如,假定在8位颜色的VGA上执行Windows,您可以呼叫CreateCompatibleBitmap。通过GetDeviceCaps,您能够确定您正处理一个有1个颜色位面和每图素8位的设备。一个字节储存一个图素。但是图素值0x37是什么意思呢?很明显是某种颜色,但到底是什么颜色呢?

图素实际上并不涉及任何固定的颜色,它只是一个值。DDB没有颜色表。问题的关键在于:当DDB显示在屏幕上时,图素的颜色是什么。它肯定是某种颜色,但具体是什么颜色呢?显示的图素将与在显示卡上的调色盘查看表里的0x37索引值代表的RGB颜色有关。这就是您现在碰到的设备依赖性。

不过,不要只因为我们不知道图素值的含义,就假定非单色DDB没用。我们将简要看一下它们的用途。 下一章,我们将看到SetBitmapBits和GetBitmapBits函数是如何被更有用的SetDIBits和GetDIBits函数所取代的。

因此,基本的规则是这样的:不要用CreateBitmap、CreateBitmapIndirect或SetBitmapBits来设定彩色DDB的位,您只能安全地使用这些函数来设定单色DDB的位。(如果您在呼叫GetBitmapBits期间,从其它相同格式的DDB中获得位,那么这些规则例外。)

在继续之前,让我再讨论一下SetBitmapDimensionEx和GetBitmapDimensionEx函数。这些函数让您设定(和获得)位图的测量尺寸(以0.1毫米为单位)。这些信息与位图分辨率一起储存在GDI中,但不用于任何操作。它只是您与DDB联系的一个测量尺寸标识。

内存设备内容


我们必须解决的下一个概念是内存设备内容。您需要用内存设备内容来处理GDI位图对象。

通常,设备内容指的是特殊的图形输出设备(例如视讯显示器或者打印机)及其设备驱动程序。内存设备内容只位于内存中,它不是真正的图形输出设备,但可以说与指定的真正设备「兼容」。

要建立一个内存设备内容,您必须首先有实际设备的设备内容句柄。如果是hdc,那么您可以像下面那样建立内存设备内容:

hdcMem = CreateCompatibleDC (hdc) ;
        
通常,函数的呼叫比这更简单。如果您将参数设为NULL,那么Windows将建立一个与视讯显示器相兼容的内存设备内容。应用程序建立的任何内存设备内容最终都通过呼叫DeleteDC来清除。

内存设备内容有一个与实际位映像设备相同的显示平面。不过,最初此显示平面非常小-单色、1图素宽、1图素高。显示平面就是单独1位。

当然,用1位的显示平面,您不能做更多的工作,因此下一步就是扩大显示平面。您可以通过将一个GDI位图对象选进内存设备内容来完成这项工作,例如:

SelectObject (hdcMem, hBitmap) ;
        
此函数与您将画笔、画刷、字体、区域和调色盘选进设备内容的函数相同。然而,内存设备内容是您可以选进位图的唯一一种设备内容型态。(如果需要,您也可以将其它GDI对象选进内存设备内容。)

只有选进内存设备内容的位图是单色的,或者与内存设备内容兼容设备有相同的色彩组织时,SelectObject才会起作用。这也是建立特殊的DDB(例如有5个位面,且每图素3位)没有用的原因。

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -