DC操作同窗体一样,取得DC句柄,即hDC.也是Long型
一个窗体对应一个DC,没有DC的窗体(控件也叫窗体),可以根据它的句柄来创建一个DC(上升到对象概念)。
DC在内存中是很占资源的,所以没有必要时,要进行释放它。
DC与windowDC是不同上,windowDC包含最上面的非工作区(一般由系统控制),如上面的图。各自绘图的区域就限制了。
看一个例子:先看图:
是有点奇怪,绘图跑到了最上面:),因为用了windowDC
Option Explicit '取得窗体客户区DC Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long '释放DC Private Declare Function ReleaseDC _ Lib "user32" (ByVal hwnd As Long,_ ByVal hdc As Long) As Long '取得窗体DC Private Declare Function GetWindowDC Lib "user32" (ByVal hwnd As Long) As Long '画多边形 Private Declare Function Polygon _ Lib "gdi32" (ByVal hdc As Long,_ lpPoint As POINTAPI,_ ByVal nCount As Long) As Long Private Type POINTAPI x As Long y As Long End Type Private Sub Command1_Click() '文本框上绘图 Dim point(2) As POINTAPI point(0).x = 10 point(0).y = 10 point(1).x = 30 point(1).y = 30 point(2).x = 30 point(2).y = 10 Dim hdc As Long hdc = GetDC(Text1.hwnd) Polygon hdc,point(0),ByVal 3& ReleaseDC Text1.hwnd,hdc End Sub Private Sub Command2_Click() '在客户区上绘图 Dim point(2) As POINTAPI point(0).x = 50 point(0).y = 50 point(1).x = 100 point(1).y = 100 point(2).x = 100 point(2).y = 50 Polygon Form1.hdc,ByVal 3& End Sub Private Sub Command3_Click() '在整个窗体上绘多边形 Dim point(2) As POINTAPI point(0).x = 10 point(0).y = 10 point(1).x = 30 point(1).y = 30 point(2).x = 30 point(2).y = 10 Dim hdc As Long hdc = GetWindowDC(Form1.hwnd) Polygon hdc,ByVal 3& ReleaseDC Form1.hwnd,hdc End Sub
可以看到DC与hwnd密切相关。有了hwnd才有DC,换言之:必须先有窗体才能有DC的存在。
特殊的:hwnd为0时代表的是整个屏幕(也是一个窗体),所以GetDc(0)就是取得屏幕的DC,屏幕保护程序就可以用它了。
=============================================================================================
再来一个概念:CreateCompatibleDC
建立兼容DC,有些称为建立存储器DC,或内存DC。
这是为什么呢?
有了DC和windowDc不是就好了么?
一个窗体对应一个DC,这是一个好事,让我们辨别了各自的区域,绘图时就不会搞错。
但是另一个麻烦来了:每操作一下DC,就会在对应的窗体上更新,频繁更新的结果就是屏幕不断地刷新,看到屏幕不断地闪烁
非常影响人的视觉和情绪。
于是CompatibleDC出来了,它不对应任何一个窗体,但它可以创建一个和窗体一模一样的数据结构的DC。创建的目的是什么呢?
就是先把频繁多次操作DC的动作先“画”到这个替身CompatibleDC上,画完后,再直接把CompatibleDC拷贝到DC上。
好了,这样多次的DC操作就简化成只更新一次,就不会造成频繁闪烁的情况。
可以说替身CompatibleDC就像我们的临时变量一样,完成了更新,它的使命就算完成了,就可以寿终正寝了。
所以,叫内存DC,是表明它与窗体是脱钩的,只单独存在于内存。故它的释放不是ReleaseDC,而是用DeleteDC来删除。
其实,如果,假如ComatibleDC有窗体联系的,它每次操作也会造成闪烁,正因为它是与窗体没有联系的,所以就没有效果显示出来。
定义:
Private Declare Function CreateCompatibleDCLib "gdi32" Alias "CreateCompatibleDC" (ByVal hdc As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" Alias "DeleteDC" (ByVal hdc As Long) As Long
hMemDC=CreateComatibleDC(hDC) '根据已有的DC来创建内存DC
DeleteDC hMemDC
结论:可以把DC和ComatibleDC“看作”就是窗体本身。
===============================================================================================
非常重要的API
BitBlt :图像转移 Bit-Block Transfer 位块传输,就是把源矩形区域图像传输到目的矩形区域。
返回值:0失败,非0成功
'取得窗体客户区DC Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long '图像转移 Private Declare Function BitBlt _ Lib "gdi32" (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 dwRop As Long) As Long 'hDestDC 目的DC,(x,y)目的DC的起始坐标 '(nWidth,nHeight)目的和源DC的矩形宽高 'hSrcDC 源DC, (xSrc,ySrc)源DC的起始坐标 'dwRop 图像转换方式,vbSrcCopy 源拷贝方式 Private Sub Command1_Click() '体验图片转换功能 BitBlt Me.hdc,Picture1.ScaleWidth / Screen.TwipsPerPixelX,Picture1.ScaleHeight / Screen.TwipsPerPixelY,Picture1.hdc,vbSrcCopy ' BitBlt Picture1.hdc,Picture1.ScaleWidth,Picture1.ScaleHeight,Me.hdc,vbSrcCopy End Sub Private Sub Command2_Click() '抓屏功能 Dim sDc As Long Me.Hide '隐藏窗体本身 DoEvents '延迟,以便抓屏准备 sDc = GetDC(0) Picture1.ScaleMode = vbPixels '和下面两句配套,都用像素 Picture1.Width = Screen.Width '和保存图片配套,这样图片就是整个图片,因为image只是视窗部分。 Picture1.Height = Screen.Height Picture1.AutoRedraw = True '内存持久,内存中要保留, '抓屏 BitBlt Picture1.hdc,Screen.Width,Screen.Height,sDc,vbSrcCopy '保存图片 Set Picture1.Picture = Picture1.Image '关键,转到picture对象上 SavePicture Picture1,"D:\3.jpg" ReleaseDC 0,sDc Picture1.AutoRedraw = False '释放内存图像 Me.Show End Sub
再回看一下为什么有picture1.autoredraw呢?
这个就有点类似前面说的CompatibleDC即内存DC,极似双缓冲DC,就是把图像保存在内存DC中,这样图像就不会因此丢掉。
那为什么有时人消失图像呢?当这个窗体被挡住时,相当于被挡住的部分或全部在DC里面“绘制”,绘制成没有图像,所以这个时候DC
就像相处曝光一样,被冲了没,DC里面就没有“内容”了,当然重显示的时候,就没有了。
hMemDC到DC的复制,不是它们的句柄进行赋值。
而是用bitBlt进行图像转移: BitBlt DC,w,h,hMemDC,vbSrcCopy
======================================================================
画图的方式:
dwROP : Raster OPeration code光栅操作码
SrcCopy 源DC覆盖拷贝到目的DC
SrcPaint 源DC与目的DC进行Or运算
SrcAnd ...................................And....
SrcInvert ....................................Xor.....
SrcErase S And (Not D)
NotSrcCopy Not S后覆盖到D
NotSrcErase Not(S or D)
MergeCopy S与花色AND后,覆盖写到DC
MergePaint (NOT S) or D
PatCopy 花色覆盖写到目的DC
PatPaint 源反相后与目的及花色进行OR (Not S) or D or P
PatInvert 目的DC与花色进行Xor D Xor P
DstInvert 目的DC反相 Not D
Blackness 目的Dc变黑 0
Whiteness 目的DC变白 1
(D表示目的DC,S表示源DC)
由此可看出DC就象一个画板,里面准备了画笔,位图,字体、区域等。根据这些素材和动作,画出一幅完美的图,就是窗体。
也有人说,DC就象一个万能的画板,可以随时画,也可随时擦掉。
上面的花色就是由一些对象组成:
CreateBitmap 建立位图对象
Private Declare Function CreateBitmap Lib "gdi32" Alias "CreateBitmap" (ByVal nWidth As Long,ByVal nHeight As Long,ByVal nPlanes As Long,ByVal nBitCount As Long,lpBits As Any) As Long
CreatePatternBrush 建立花色对象(画笔)
Private Declare Function CreatePatternBrush Lib "gdi32" Alias "CreatePatternBrush" (ByVal hBitmap As Long) As Long
这两个API就建立了一个花色对象。但怎么用这个对象呢?
前面bitblt中并没有指定花色对象的参数,它是怎么联系哪个花色对象的呢?
为DC选用对象: SelectObject
Private Declare Function SelectObject Lib "gdi32" Alias "SelectObject" (ByVal hdc As Long,ByVal hObject As Long) As Long
这个就为某DC指定选用对象,于是就联系起来了,。返回值:DC中前一个同类型对象的handle
意思是:要使用临时画笔,这个就替换了原来的画笔,但使用完后,删除后,怎么恢复原来的画笔呢?就是这个返回值,指向
同类型对象的前一个handle
另外一个就是因为DC中很多对象,有些是不再使用的,为了节省资源,可以进行删除
无论是位图或画笔,可以进行删除:DeleteObject
Private Declare Function DeleteObject Lib "gdi32" Alias "DeleteObject" (ByVal hObject As Long) As Long
下例是一个屏幕变暗及恢复例子,其实没啥用处,就是看一下各API是怎么用的。
InvalidateRectAny就是恢复窗体原貌,重绘某区域。原因是:指定某矩形区域为“无效”后,系统就会不断向其区域发出“绘制”命令,使这一部分重新绘制。
但这有什么用呢?因为重绘一个大的区域会浪费时间,效率不高,于是就用这个API对“关键的”、“常变”的区域进行指定并重绘,提高绘制效率。
Private Declare Function InvalidateRect Lib "user32" Alias "InvalidateRect" (ByVal hwnd As Long,lpRect As RECT,ByVal bErase As Long) As Long
最后一个参数是是否擦除背景,就是是否删除DC,一般为真:True
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long Private Declare Function ReleaseDC _ Lib "user32" (ByVal hwnd As Long,_ ByVal hdc As Long) As Long Private Declare Function BitBlt _ Lib "gdi32" (ByVal hDestDC As Long,_ ByVal dwRop As Long) As Long Private Declare Function CreateBitmap _ Lib "gdi32" (ByVal nWidth As Long,_ ByVal nPlanes As Long,_ ByVal nBitCount As Long,_ lpBits As Any) As Long Private Declare Function CreatePatternBrush Lib "gdi32" (ByVal hBitmap As Long) As Long Private Declare Function SelectObject _ Lib "gdi32" (ByVal hdc As Long,_ ByVal hObject As Long) As Long Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long Private Declare Function InvalidateRectAsAny _ Lib "user32" _ Alias "InvalidateRect" (ByVal hwnd As Long,_ lpRect As Any,_ ByVal bErase As Long) As Long Private Sub Command1_Click() '屏幕变暗 Dim hOldPattern As Long,bBmp(0 To 15) As Byte Dim hBitmap As Long,hPattern As Long,hDCScreen As Long Dim sx As Long,sy As Long bBmp(0) = &H55: bBmp(2) = &HAA: bBmp(4) = &H55: bBmp(6) = &HAA bBmp(8) = &H55: bBmp(10) = &HAA: bBmp(12) = &H55: bBmp(14) = &HAA hBitmap = CreateBitmap(8,8,1,bBmp(0)) '建立位图 hPattern = CreatePatternBrush(hBitmap) '根据位图建立画笔(花色) hDCScreen = GetDC(0) hOldPattern = SelectObject(hDCScreen,hPattern) '选用画笔 sx = Screen.Width / Screen.TwipsPerPixelX sy = Screen.Height / Screen.TwipsPerPixelY BitBlt hDCScreen,sx,sy,hDCScreen,vbMergeCopy '图像转换 SelectObject hDCScreen,hOldPattern '恢复原来画笔 ReleaseDC 0,hDCScreen DeleteObject hBitmap '删除位图 DeleteObject hPattern '删除画笔(花色) End Sub Private Sub Command2_Click() InvalidateRectAsAny 0,ByVal 0&,True '恢复原来屏幕(变明) End Sub
可以看到vbMergeCopy与花色的运算,是前面刚选进来的画笔(花色),用完就扔delete.根据选用时的handle恢复原来的花色。
======================================================================================================
认识Bitmap对象
bitmap不是位图么?一般我们看到的就是“图”,如果直接不用函数直接要来显示的画,就麻烦了,这个“图”文件中的表头消息、位图表消息,
点阵数据等很是麻烦。
一般用LoadPicture就轻易读出。实际上它是把Bitmap当作对象,返回的是一个对象的句柄。叫hBitmap(handle of Bitmap)
是有点怪!
看一下情况: Set picture1.picture=loadpicture(位图)
这个就设置了图片,实际上是取得了hBitmap给picture属性。这个picture就是hBitmap
我们用: print Picture1.picture 可以看到显示出来是一个长整形。
(注意:bmp,jpg,gif都是hBitmap,但ico,cur,wmf时就不是了)
可以由Bitmap的句柄hBitmap取得这个bitmap图的结构。
Private Declare Function GetObject Lib "gdi32" Alias "GetObjectA" (ByVal hObject As Long,ByVal nCount As Long,lpObject As Any) As Long
根据一个对象的句柄,取得这个对象的结构。取回的结构在后面两个参数中。返回值:0失败,非0成功。
对于一个Bitmap对象,可以:
dim bm as Bitmap
GetObject hBitmap,len(bm),bm '返回值在bm中。
其中Bitmap类型定义:
Private Type BITMAP bmType As Long '固定为0 bmWidth As Long '图的宽 bmHeight As Long '图的高 bmWidthBytes As Long '多少二进制构成一个储存单元,通常为16(两字节) bmPlanes As Integer '调色板数 bmBitsPixel As Integer '每像素所占二进制位数(表示色彩丰富度) bmBits As Long '位图二进位数据的起始位置 End Type
这样通过GetObject取回结构bm后。
就可以用bm.bmwidth,bmheight取得这个图的宽和高,为后面的工作作准备。