By Defanive
通过上次GUI开发1的教程,已经可以做出很精美的画面了
接下来我们通过一段代码来引入这一次的目的
在上一次教程的结束代码中,我们做出了图标从左往右移动的代码
假设我们正在做复杂的绘图工程,绘图计算需要一定的耗时
我们在绘图操作前使用API Sleep来模拟这种计算耗时- @echo off
- CAPI
- set CAPI=API Call user32 LoadImageW ;0 $1.ico ;1 ;0 ;0 ;16
- set hIcon=%CAPI_Ret%
- set CAPI=API Call kernel32 GetConsoleWindow
- set hCMD=%CAPI_Ret%
- set CAPI=API Call user32 GetDC ;%hCMD%
- set hDC=%CAPI_Ret%
- set x=0
- :loop
- color 07
- set CAPI=API Call kernel32 Sleep ;10
- set CAPI=API Call user32 DrawIconEx ;%hDC% ;%x% ;0 ;%hIcon% ;32 ;32 ;0 ;0 ;3
- set CAPI=API Call kernel32 Sleep ;20
- set /a x+=2
- goto loop
复制代码 运行批处理,就可以看到闪烁十分严重
为了避免闪烁,就引入了内存缓冲的概念
绘制过程中,先暂时把图像绘制到内存中
全部绘制完毕了,再一次性将全部图像复制到屏幕上
这样就实现了无闪烁效果,也就是内存缓冲的目的
既然要在内存中画图,我们就需要创建一个内存中的DC
为了最后能把内存DC的内容复制到CMD窗口DC上,我们需要让这两个DC兼容
使用API CreateCompatibleDC我们可以根据CMD窗口的DC创建一个兼容的内存DC,并返回其句柄- set CAPI=API Call gdi32 CreateCompatibleDC ;%hDC%
- set hDCMem=%CAPI_Ret%
- echo %hDCMem%
复制代码 我们创建了一个与CMD窗口DC兼容的内存DC,其句柄保存在hDCMem变量中
有了“画布”DC,但是没有画纸是无法进行绘图的
于是我们要创建一个Bitmap,同样的也要与CMD窗口的DC兼容
使用API CreateCompatibleBitmap可以根据CMD窗口的DC创建一个兼容的Bitmap,并返回其句柄
创建Bitmap的时候我们会需要指定这个Bitmap的大小,再次我们指定创建一个320x320大小的Bitmap- set CAPI=API Call gdi32 CreateCompatibleBitmap ;%hDC% ;320 ;320
- set hBMPMem=%CAPI_Ret%
- echo %hBMPMem%
复制代码 于是我们创建了一个与CMD窗口DC兼容的Bitmap,并保存在了hBMPMem变量中
我们创建了内存DC,也创建了内存Bitmap
接下来就是要把Bitmap放到DC里面,以便进行画图操作
使用API SelectObject可以把对象放入到DC里,在此我们把内存Bitmap放到内存DC里- set CAPI=API Call gdi32 SelectObject ;%hDCMem% ;%hBMPMem%
复制代码 现在我们的内存缓冲已经准备好了
我们把上面的代码和上一个例程的代码整合一下
同时把所有绘图操作的目标DC都从CMD窗口DC换成内存DC
在此由于color命令只对CMD窗口DC有效,所以我们换用API Rectangle进行清理
API Rectangle会在指定DC中使用画笔和画刷填充一个矩形区域
下面是loop标签内的代码- :loop
- set CAPI=API Call gdi32 Rectangle ;%hDCMem% ;0 ;0 ;320 ;320
- set CAPI=API Call user32 DrawIconEx ;%hDCMem% ;%x% ;0 ;%hIcon% ;32 ;32 ;0 ;0 ;3
- set CAPI=API Call kernel32 Sleep ;20
- set /a x+=2
- goto loop
复制代码 我们把全部对hDC的操作都改成了对hDCMem操作
于是我们实现了把绘图过程全部放在了内存中进行
现在我们的全部绘图过程都画在了内存DC中
最后一步就是绘图完成之后把内存DC的内容复制到CMD窗口DC上
通过API BitBlt可以把一个DC中的内容复制到另外一个DC中
整个例程代码如下- @echo off
- CAPI
- set CAPI=API Call user32 LoadImageW ;0 $1.ico ;1 ;0 ;0 ;16
- set hIcon=%CAPI_Ret%
- set CAPI=API Call kernel32 GetConsoleWindow
- set hCMD=%CAPI_Ret%
- set CAPI=API Call user32 GetDC ;%hCMD%
- set hDC=%CAPI_Ret%
- set CAPI=API Call gdi32 CreateCompatibleDC ;%hDC%
- set hDCMem=%CAPI_Ret%
- set CAPI=API Call gdi32 CreateCompatibleBitmap ;%hDC% ;320 ;320
- set hBMPMem=%CAPI_Ret%
- set CAPI=API Call gdi32 SelectObject ;%hDCMem% ;%hBMPMem%
- set x=0
- :loop
- set CAPI=API Call gdi32 Rectangle ;%hDCMem% ;0 ;0 ;320 ;320
- set CAPI=API Call user32 DrawIconEx ;%hDCMem% ;%x% ;0 ;%hIcon% ;32 ;32 ;0 ;0 ;3
- set CAPI=API Call gdi32 BitBlt ;%hDC% ;0 ;0 ;320 ;320 ;%hDCMem% ;0 ;0 ;13369376
- set CAPI=API Call kernel32 Sleep ;20
- set /a x+=2
- goto loop
复制代码 API BitBlt的参数稍微复杂
第1、6个参数为目标DC和源DC,再次为CMD窗口的DC和内存DC
第2、3个参数为目标DC的目标坐标x和y,也就是复制到哪里,在此我们把内存DC的内容复制到CMD窗口DC的(0,0)处,也就是左上角
第4、5个参数为复制的大小宽和高,在此我们把内存DC中的全部内容都复制过去,因此我们给参数320 320
第7、8个参数为源DC的源坐标x和y,也就是从源DC的哪里开始复制,在此我们从左上角开始复制,也就是0 0
最后一个参数为操作方法,其值可以参考MSDN,再次我们选择SRCCOPY,也就是复制内容,值为0xCC0020,也就是13369376
运行批处理,我们可以看到图像绘制非常平滑,没有闪烁
这对于大型的GUI开发程序是非常有实用性的
大型程序绘图分多层进行,如背景、角色、文字、遮罩等等
如果没有内存缓冲将会有非常严重的闪烁
内存缓冲解决闪烁是非常有效的
链接: https://pan.baidu.com/s/1XQakFk2fDSft2I69kdOmgg?pwd=7hkn |