1. 影响VB窗口中位图的属性
2) Picture属性:该属性用于作为窗口背景的图片,在窗口内部,该属性封装了一个BITMAP的GDI对象。
3) hDC、Image及AutoReDraw属性:
a) 如果设置为AutoReDraw(True) 则:
Ø hDC为1个内存DC,Image属性值是选入hDC的位图句柄,此时GetCurrentObject(hDC,OBJ_BITMAP)=Image(朋友们自行验证)。
Ø 在窗口输出操作代码入Print、Circle等输出到内存DC中,然后再从内存DC拷贝到目标窗口。
Ø VB没有提供目标窗口的DC,如果需要目标窗口的DC,需要使用GetDC获取,使用ReleaseDC释放。
Ø 如果使用API输出到hDC,输出后,必须使用Refresh将输出反映到目标窗口,如果使用VB自带的绘图及文字输出方法,则不需要使用Refresh。
Ø 不会产生Paint事件,对应于Windows的Paint消息,只是简单的将Image的内容拷贝到目标窗口。取代之,是在运行开始,会产生Show事件,可以在Show事件。
Ø 如果使用API的GetDIBits方法,将获取窗口的前景及背景内容。
Ø 事实上,AutoReDraw(True)时,窗口绘画就是一种双缓冲机制,但此机制有缺陷,其可绘图空间VB被限制和窗口尺寸一致,无法大于窗口尺寸。
b) 如果设置为非AutoReDraw (False) 则:
Ø hDC是一个和窗口关联的设备DC
Ø Image属性仍然和一个位图对象关联,只是这个位图的内容由BackColor属性及Picture属性决定,每次BackColor或者Picrure改变,则会重新生成Image对应的位图对象。BackColor属性在Picture不能完全填充窗口时候填充Picture之外的部分。
Ø Image对象将作为目标窗口的背景。CLS操作,就是讲Image对象内容拷贝到目标窗口。
Ø 对应于Windows的Paint消息,将产生VB的Paint事件,VB程序一般在该事件中,提供绘画窗口的代码,实现输出。
Ø 如果使用API的GetDIBits方法,只能获取窗口的背景内容(BackColor及Picture属性相关)。(AutoReDraw (False)时,如何获取前景的内容,后续介绍中将给出代码。)
Ø
无论AutoReDraw为何,当窗口尺寸改变时,将重建Image,以使得Imge的大小和窗口一致
c) AutoReDraw由True变为False,则True状态下的绘画输出,在变为AutoReDraw(False)后,绘图输出将成为背景的一部分,此Cls,将不能清除True时的绘画输出;要清除True时的绘图输出,则只再设置AutoReDraw(True)
d) AutoReDraw由False变为True: VB会使用BackColor及Picture属性值重新加载背景,前景将被取消
2. 获取完整的窗口位图数据:
在AutoReDraw(True)时,Image实际上是一个和窗口内容一致的位图句柄,可以直接将Image属性直接赋值给另一个控件或窗口的Picture属性,或者以Image属性作为句柄,使用API调用获取窗口内容数据;此时,一般都能得到窗口背景及前景的位图内容。
Picture2.Picrure=Picture1.Image ‘Picture1上显示的内容拷贝到Picture2的Picrure属性中
或者:
With Picture1
nLines=GetDiBits(.Hdc,.Image,mImgLine,ImgData(0),bmpInfoHeader,BI_RGB)
nLines=GetDiBits(.Hdc,Picture2.Image,BI_RGB)
end with
但是,使用Image属性获取位图,是无法获取下面几种情况的位图的:
1) 在非AutoReDraw(False)时,使用Image属性只能得到背景的图像,使用VB绘图方法及在hDC(窗口或者控件的hDC属性)上使用API绘图输出,这些输出都不能通过Image属性获得。
2) 在AutoReDraw(True)时,如果绘图操作是使用API输出到窗口的DC(不是控件的hDC,是使用hDCx=GetDC()得到的窗口DC)中,则该部分的位图内容是无法通过Image属性获取。同时,读取Image属性值的时候,VB会重新使用Image的内容更新窗口显示,从而使得窗口丢失使用上述方法的前景的输出。
3) 如果窗口或者控件上,有别的控件,那么这些控件虽然显示在窗口上,但是是不能通过Image属性获得。
因此,要想获得窗口的所有位图,只有使用hWnd属性而获得真正的窗口DC(称之为hDCx),然后通过hDCx的位图句柄hBmp,再通过hBmp获取位图数据;但是问题没有这么简单,因为Windows不允许直接读取窗口DC的位图数据,因此,还得将窗口位图拷贝到内存位图中后,才能读取位图数据。以下是完整的使用DIB读取窗口位图的函数。
Private Function GetWindowBmp(hWnd As Long,BmpInfo As BitmapInfo,nBits As Long,Optional mOffset As Long = 0) As Byte()
Dim hDCx As Long
Dim hBmp As Long
Dim r As Long
Dim hMemDC As Long
Dim nWidth As Long,nHeight As Long
Dim Rect As Rect
Dim ImgData() As Byte
Dim mLine As Long
Dim mLineBytes As Long
Call GetWindowRect(hWnd,Rect)
nWidth = Rect.Right - Rect.Left
nHeight = Rect.Bottom - Rect.Top
hDCx = GetDC(hWnd)
hMemDC = CreateCompatibleDC(hDCx)
hBmp = CreateCompatibleBitmap(hDCx,nWidth,nHeight)
r = SelectObject(hMemDC,hBmp)
r = BitBlt(hMemDC,nHeight,hDCx,vbSrcCopy)
With BmpInfo.bmiHeader
.biSize = Len(BmpInfo.bmiHeader)
.biWidth = nWidth
.biHeight = nHeight
.biPlanes = 1
.biBitCount = nBits
.biCompression = BI_RGB
End With
mLineBytes = (((nWidth * nBits) + &H1F) And &HFFFFFFE0) \ &H8
ReDim ImgData(mLineBytes * nHeight - 1 + mOffset)
mLine = GetDIBits(hDCx,hBmp,ImgData(mOffset),BmpInfo,BI_RGB)
GetWindowBmp = ImgData
DeleteDC hMemDC
ReleaseDC hWnd,hDCx
DeleteObject hBmp
End Function
该函数返回窗口hWnd上显示的所有位图数据的数组,位图数据在该数组的mOffset位置开始放置,mOffset前的字节为保留字节(后面将介绍这些保留字节的用途),参数BmpInfo是一个BitmapInfo的数据结构,调用时候只要定义,不需给结构成员赋值,函数会按照窗口的位图数据自动填充,函数的调用者,一般需要使用该结构返回的数据。
3. 对获取位图数据进行处理:
以下代码用于获取Pic1的位图数据到一个数组中,然后将其转换为灰度图像的数据,再将灰度 图像拷贝到Pic2中进行显示,显示时,我们使用Pic2的Image属性,使用该属性能将拷贝入的位图保持住。
Private Sub Command3_Click()
Dim ImgData() As Byte
Dim mLineBytes As Long
Dim BitmapInfo As BitmapInfo
Dim mLineBytesA As Long
Dim mIdx As Long
Dim x As Long,y As Long,C As Long
Dim mLineFromIdx As Long
Dim mBytesPerPix As Long
ImgData = GetWindowBmp(Pic1.hWnd,BitmapInfo,32&) '获取Pic1窗口上显示的位图数据
With BitmapInfo.bmiHeader
'计算每行字节数
mLineBytes = .biWidth * .biBitCount / 8
If mLineBytes Mod 4 <> 0 Then
mLineBytesA = ((mLineBytes + 3) \ 4) * 4
Else
mLineBytesA = mLineBytes
End If
mBytesPerPix = .biBitCount / 8 '每像素字节数
For y = 0 To .biHeight - 1
mIdx = mLineFromIdx
For x = 0 To mLineBytes - 1 Step mBytesPerPix
C = ImgData(mIdx)
C = (C + ImgData(mIdx + 1) + ImgData(mIdx + 2)) / 3
ImgData(mIdx) = C
ImgData(mIdx + 1) = C
ImgData(mIdx + 2) = C
mIdx = mIdx + mBytesPerPix
Next
mLineFromIdx = mLineFromIdx + mLineBytesA
Next
End With
SetDIBits 0,Pic2.Image,BitmapInfo.bmiHeader.biHeight,BI_RGB
‘如果Pic2.Image改为Pic1.Image则,将Pic1的彩色改变为黑白
Pic2.Refresh
End Sub