GDI是什么?
前面,我们利用windows的图形设备接口实现了在控制台窗口中作图和动画。其中,链接了gdi32.lib
库,也就是使用了GDI(图形设备接口)。
GDI在全称是Graphics Device Interface,是图形显示与实际物理设备之间的桥梁。GDI使得用户无需关心具体设备的细节,而只需在一个虚拟的环境(即逻辑设备)中进行操作。
GDI函数大致可分类为:
- 设备上下文函数(如GetDC、CreateDC、DeleteDC)
- 画线函数(如LineTo、Polyline、Arc)
- 填充画图函数(如Ellipse、FillRect、Pie)
- 画图属性函数(如SetBkColor、SetBkMode、SetTextColor)
- 文本、字体函数(如TextOut、GetFontData)
- 位图函数(如SetPixel、BitBlt、StretchBlt)
- 坐标函数(如DPtoLP、LPtoDP、ScreenToClient、ClientToScreen)
- 映射函数(如SetMapMode、SetWindowExtEx、SetViewportExtEx)
- 元文件函数(如PlayMetaFile、SetWinMetaFileBits)
- 区域函数(如FillRgn、FrameRgn、InvertRgn)
- 路径函数(如BeginPath、EndPath、StrokeAndFillPath)
- 裁剪函数(如SelectClipRgn、SelectClipPath)
用LoadImage
读取位图bmp文件
从位图文件路径读取bmp文件函数LoadImage
:
LoadImage:可以加载位图、图标、光标多种图像数据,既可以从资源视图中加载,也可以从磁盘中直接加载。函数
HANDLE LoadImage(
HINSTANCE hinst,
LPCTSTR lpszName,
UINT uType, // 类型:位图、图标、光标
int cxDesired, //x坐标位置
int cyDesired,//y坐标位置
UINT fuLoad
);
例如:
HBITMAP hbmpBack = (HBITMAP) ::LoadImage (
NULL,
".\\xxxx.bmp",
IMAGE_BITMAP,
0,
0,
LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
将位图选入内存兼容区
设当前控制台窗口的句柄为hWnd
,则对应的HDC是设备描述表句柄用:
HDC hdc = GetDC(hWnd);
来获得。通过hdc
可创建对应的内存兼容区hdcMem
:
//依据屏幕显示DC创建内存DC设备描述表句柄
HDC hdcMem = CreateCompatibleDC(hdc);
设hdcMem为内存DC设备描述表句柄,用SelectObject函数可将位图句柄hbmpBack
放到hdcMem中。
SelectObject(hdcMem,hbmpBack);
SelectObject函数选择一对象到指定的设备上下文环境中,该新对象替换先前的相同类型的对象。
为了保存旧的位图句柄(以备恢复现场),通常用:
HBITMAP hOldBmp = (HBITMAP)::SelectObject(hdcMem,hbmpBack);
将内存兼容区拷贝到屏幕区
Windows不允许直接将位图绘制到需要显示的窗口DC(设备上下文)上,只能将位图先放入兼容的设备上下文中(兼容DC),然后将兼容上下文的内容拷贝到设备上下文中,才能实现位图的绘制。
使用BitBlt或者StretchBlt两个API,可实现将兼容DC(源DC)中的内容拷贝目标DC(需要显示的DC)中。他们的区别是,BitBlt不能进行位图的缩放功能,而Stretch能实现缩放。
【BitBlt】函数
BOOL BitBlt(
HDC hdcDest, // 目标DC句柄
int nXDest, int nYDest, int nWidth, int nHeight, // 目标区域
HDC hdcSrc, // 源DC句柄
int nXSrc, int nYSrc, //源区域的左上角
DWORD dwRop // 操作的方式,一般为SRCCOPY(拷贝)
);
【StretchBlt】函数
BOOL StretchBlt(
HDC hdcDest, //目标DC的句柄
int xDest, int yDest, int wDest, int hDest, //目标DC的区域
HDC hdcSrc, //源DC的句柄
int xSrc, int ySrc, int wSrc, __in int hSrc, //源DC的区域
DWORD rop //操作标志,一般为SRCCOPY,意思为拷贝
);
恢复现场
作图完成后,应恢复现场。
将现在的位图句柄选出临时内存DC,也就是将我们原来的位图句柄选入内存DC中。这里为什么需要选出来?如果不选出来,当前的位图句柄还在内存DC中,使用DeleteDC后将会同时删除现在使用的位图句柄。
::SelectObject(hdcMem, hOldBmp);
销毁临时的内存DC
::DeleteDC(hdcMem);
实例 ( showbmp.cpp)
VC6.0 wtclablogo.bmp
为436*80 的24位位图。实例中演示了BitBlt
和StretchBlt
的用法。
#include<windows.h>
#include<stdio.h>
#pragma comment(lib,"user32.lib")
#pragma comment(lib,"gdi32.lib")
extern "C" WINBASEAPI HWND WINAPI GetConsoleWindow ();
int main(int argc,char *argv[])
{
HDC hdc = GetDC(GetConsoleWindow ());//HDC是设备描述表句柄(获取屏幕显示DC)
HBITMAP hbmpBack = (HBITMAP) LoadImage (NULL,
("wtclablogo.bmp"),
IMAGE_BITMAP,
0, 0,
LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hOldBmp = (HBITMAP)SelectObject(hdcMem,hbmpBack);
system("cls");
BitBlt(hdc,200,300,800,600,hdcMem,0,0,SRCCOPY);
SetStretchBltMode(hdc,HALFTONE);
StretchBlt(hdc,20,50,180,40,hdcMem,0,0,436,80,SRCCOPY);
//恢复
SelectObject(hdcMem, hOldBmp);
DeleteDC(hdcMem);
return 0;
}
编译:(或直接在VC6 集成环境中编译。将图片wtclablogo.bmp
放在exe文件同一目录下)
cl.exe showbmp.cpp
编译和执行截屏:
最简代码(15行):
#include<windows.h>
#pragma comment(lib,"user32.lib")
#pragma comment(lib,"gdi32.lib")
extern "C" WINBASEAPI HWND WINAPI GetConsoleWindow ();
void main(int argc,char *argv[])
{
HDC hdc = GetDC(GetConsoleWindow ());//HDC是设备描述表句柄(获取屏幕显示DC)
HBITMAP hbmpBack = (HBITMAP) LoadImage (NULL,"wtclablogo.bmp",IMAGE_BITMAP,0, 0,
LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
HDC hdcMem = CreateCompatibleDC(hdc);
SelectObject(hdcMem,hbmpBack);
system("cls");
BitBlt(hdc,200,300,800,600,hdcMem,0,0,SRCCOPY);
StretchBlt(hdc,20,50,180,40,hdcMem,0,0,436,80,SRCCOPY);
}
进一步的改进方向
显然,通过命令行传参,就可实现在控制台窗口显示指定文件名的bmp图片了。但GDI 只能显示bmp, icon, cursor, animated cursor等图形文件,而我们常常要显示jpg, png等更多格式的图片,此时GDI就无能为力了。GDI是20年前的技术。
windows2000之后,GDI就升级为更强大的GDIplus(GDI+)了。通过GDI+ 技术可以显示更多格式的图片。
【注1】在使用StretchBlt拉伸图像时,可用SetStretchBltMode来提高显示质量。
SetStretchBltMode(hdc,HALFTONE);
【注2】也可用TransparentBlt来实现伸缩。TransparentBlt可处理透明特性。TransparentBlt可以根据目标dc的矩形大小和原dc矩形大小比例对位图进行伸缩处理,可设置掩码色,也就是实现透明贴图 ,该函数为系统API函数。如:
TransparentBlt(hdc,20,50,180,40,hdcMem,0,0,436,80,SRCCOPY);
注意,使用TransparentBlt需加入Msimg32.lib
库。否则编译会出错。
#pragma comment(lib,"Msimg32.lib")
使用TransparentBlt的源码如下:
#include<windows.h>
#pragma comment(lib,"user32.lib")
#pragma comment(lib,"gdi32.lib")
#pragma comment(lib,"Msimg32.lib")
extern "C" WINBASEAPI HWND WINAPI GetConsoleWindow ();
void main(int argc,char *argv[])
{
HDC hdc = GetDC(GetConsoleWindow ());//HDC是设备描述表句柄(获取屏幕显示DC)
HBITMAP hbmpBack = (HBITMAP) LoadImage (NULL,"wtclablogo.bmp",IMAGE_BITMAP,0, 0,
LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
HDC hdcMem = CreateCompatibleDC(hdc);
SelectObject(hdcMem,hbmpBack);
system("cls");
BitBlt(hdc,200,300,800,600,hdcMem,0,0,SRCCOPY);
TransparentBlt(hdc,20,50,180,40,hdcMem,0,0,436,80,SRCCOPY);
}
此外,以上程序绘图只绘一次,窗口变化后绘图将被擦除,为了能不断重绘刷新,可采用while循环(for(;;)等价
)和sleep函数,以使每隔一定时间能自动刷新。程序中,退出while循环的方法是:按ESC键(采用kbhit()
检测)或Ctrl+C。代码如此下:
#include<windows.h>
#include<conio.h>
#pragma comment(lib,"user32.lib")
#pragma comment(lib,"gdi32.lib")
#pragma comment(lib,"Msimg32.lib")
extern "C" WINBASEAPI HWND WINAPI GetConsoleWindow ();
void main(int argc,char *argv[])
{
for(;;){
HDC hdc = GetDC(GetConsoleWindow ());//HDC是设备描述表句柄(获取屏幕显示DC)
HBITMAP hbmpBack = (HBITMAP) LoadImage (NULL,"wtclablogo.bmp",IMAGE_BITMAP,0, 0,
LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
HDC hdcMem = CreateCompatibleDC(hdc);
SelectObject(hdcMem,hbmpBack);
system("cls");
BitBlt(hdc,200,300,800,600,hdcMem,0,0,SRCCOPY);
TransparentBlt(hdc,20,50,180,40,hdcMem,0,0,436,80,SRCCOPY);
Sleep(100);
if (kbhit())//检查是否有按键按下
{
if (_getch() == 0x1b)break;//若按下ESC键跳出循环
}
}
}
效果如下: