基于VC6.0的控制台作图--显示位图(bmp)

文章目录

    • GDI是什么?
    • 用`LoadImage`读取位图bmp文件
    • 将位图选入内存兼容区
    • 将内存兼容区拷贝到屏幕区
    • 恢复现场
    • 销毁临时的内存DC
    • 实例 ( showbmp.cpp)
    • 进一步的改进方向

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位位图。实例中演示了BitBltStretchBlt的用法。

#include
#include
#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

编译和执行截屏:

基于VC6.0的控制台作图--显示位图(bmp)_第1张图片

最简代码(15行):

#include
#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
#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
#include
#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键跳出循环
		}
	}
}

效果如下:

基于VC6.0的控制台作图--显示位图(bmp)_第2张图片

你可能感兴趣的:(C语言开发应用)