VC++控制台应用程序下使用GDI在内存中画图
2012-11-05 13:55
876 查看
VC++控制台应用程序下使用GDI在内存中画图
很多时候,我们并不需要将绘制的图像显示在窗口,只需要将图像绘制在内存中,然后将内存中的图像保存成图片文件。说到内存绘制,也许有人会说,我直接创建一块内存像素数据,然后计算每个图形的像素结构,填充内存像素数据不就可以了么,没有必要使用GDI呀,对于大部分几何图像来说,这么做完全没问题,但是如果你想绘制字符的时候,这么做就很困难了,因为你很难去计算一个字符的像素结构,所以当你需要绘制字符的时候,你还是得使用GDI。
接下来,我将介绍:(1)如何在控制台应用程序下使用GDI在内存中画图;(2)将内存中的图像存储为图片。
程序运行结果如图1所示:
图1:程序运行结果
1 背景
以前写MFC应用程序的时候,就接触到GDI了。只不过那个时候,Visual Studio已经帮你生成了MFC应用的基本框架,在Visual Studio生成的View文件里,存在一个方法::OnDraw(CDC* /*pDC*/),Visual Studio会添加注释“// TODO: 在此处为本机数据添加绘制代码”,即是说,请在此处调用CDC进行绘制操作。CDC的创建和初始化MFC都帮你完成了,你只需要添加一些个性化代码即可,当然,这对于一个MFC应用来说,当然够了,但是如果你想写一个不使用MFC的应用,我该怎么获得CDC呢?很多时候,我们并不需要将绘制的图像显示在窗口,只需要将图像绘制在内存中,然后将内存中的图像保存成图片文件。说到内存绘制,也许有人会说,我直接创建一块内存像素数据,然后计算每个图形的像素结构,填充内存像素数据不就可以了么,没有必要使用GDI呀,对于大部分几何图像来说,这么做完全没问题,但是如果你想绘制字符的时候,这么做就很困难了,因为你很难去计算一个字符的像素结构,所以当你需要绘制字符的时候,你还是得使用GDI。
接下来,我将介绍:(1)如何在控制台应用程序下使用GDI在内存中画图;(2)将内存中的图像存储为图片。
2 使用GDI在内存中画图
GDI是Windows API,因此只要是Win32程序(包括Win32应用程序和Win32控制台程序),都可以使用GDI。为了简单起见,如果你想运行本文介绍的代码,新建一个空的Win32控制台程序即可。我觉得在代码中加注释比在这描述更加明了,那么让我们看看代码吧:#include <Windows.h> #include <tchar.h> #include <stdio.h> /** * 将HBITMAP保存为BMP图片,实现细节见第三节 */ static void hbitmapToImage(HDC hdc, HBITMAP hbm); int main(int argc, char *argv[]) { // 创建绘制环境 HDC memDC = CreateCompatibleDC(NULL); // 创建画布 HBITMAP hbm = CreateBitmap(100, 100, 1, 32, NULL); // 创建一个字体,其中40是字体大小,参数可以根据自己需求调整 HFONT hfont = CreateFont(40, 0, 0, 0, FW_THIN, false, false, false, DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY, FF_MODERN, _T("宋体")); // 选择画布和字体,你可以保存之前的画布和字体用于恢复,为了篇幅起见,在此就不这样做了 SelectObject(memDC, hbm); SelectObject(memDC, hfont); // 开始绘制操作 RECT imgRect = {0, 0, 100, 100};// 设置为画布的宽高 SIZE fontPixSize; // 字符的像素大小,对于一种字体来说,其字符的高是一致的,但是宽基本不同 // 将背景填充为黑色 FillRect(memDC, &imgRect, (HBRUSH)GetStockObject(BLACK_BRUSH)); // 得到字符串的像素宽高,可以为任意字符串 GetTextExtentPoint32(memDC, _T("我"), 1, &fontPixSize); SetTextColor(memDC, RGB(255, 255, 255)); // 设置文本颜色 SetBkMode(memDC, TRANSPARENT); // 设置背景模式 // 计算绘制起始位置,使字符在画布的中间;当然你也可以添加更多的绘制操作,如PolyDraw,LineTo等 TextOut(memDC, (100 - fontPixSize.cx) / 2, (100 - fontPixSize.cy) / 2, _T("我"), 1); // 将HBITMAP保存为BMP图片 hbitmapToImage(memDC, hbm); // 释放资源 DeleteObject((HGDIOBJ)hfont); DeleteObject((HGDIOBJ)hbm); DeleteDC(memDC); return 0; }
3 将HBITMAP保存为BMP图片
第2节描述了如何使用GDI在内存中画图,本节介绍如何将HBITMAP保存为BMP图片。代码如下:/** * 将HBITMAP保存为BMP图片 */ void hbitmapToImage(HDC hdc, HBITMAP hbm) { // 得到hbm中每个像素所占的位数 int bmPixBitCount = GetDeviceCaps(hdc, BITSPIXEL) * GetDeviceCaps(hdc, PLANES); int wBitCount = 0; if(bmPixBitCount <= 1) wBitCount = 1; else if(bmPixBitCount <= 4) wBitCount = 4; else if(bmPixBitCount <= 8) wBitCount = 8; else wBitCount = 24; ////////////////////////////////////////////////////////////////////////// BITMAP bm; GetObject(hbm, sizeof(bm), &bm); int bmRowCount = ((bm.bmWidth * wBitCount + 31) / 32) * 4; // 一行所占的字节数,BMP像素数据按四字节对齐 int bmByteCount = bmRowCount * bm.bmHeight; // 像素数据的大小 char *buf = new char[bmByteCount]; // 位图信息头结构,定义参考MSDN BITMAPINFOHEADER bi; bi.biSize= sizeof(BITMAPINFOHEADER); bi.biWidth = bm.bmWidth; bi.biHeight = bm.bmHeight; bi.biPlanes = 1; bi.biBitCount = wBitCount; bi.biCompression= BI_RGB; bi.biSizeImage=0; bi.biXPelsPerMeter = 0; bi.biYPelsPerMeter = 0; bi.biClrImportant = 0; bi.biClrUsed = 0; GetDIBits(hdc, hbm, 0, bm.bmHeight, buf, (BITMAPINFO*)(&bi), DIB_RGB_COLORS); // 得到像素值,保存在buf中 // 位图文件头结构,定义参考MSDN BITMAPFILEHEADER bf; bf.bfType = 0x4D42; // BM bf.bfSize = bmByteCount; bf.bfReserved1 = 0; bf.bfReserved2 = 0; bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); // 写入文件 FILE *bmFile = fopen("demo.bmp", "w+b"); if (bmFile) { fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, bmFile); // 写入位图文件头 fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, bmFile); // 写入位图信息头 fwrite(buf, sizeof(char), bmByteCount, bmFile); // 写入像素数据 fclose(bmFile); } delete []buf; }
程序运行结果如图1所示:
图1:程序运行结果
相关文章推荐
- VC++窗口应用程序中使用控制台输出
- 使用VC2008 Express编写Win32控制台应用程序及其安装(初学者适用)
- VC++使用gdi+画图导致内存不断增加的问题
- VC++窗口应用程序中使用控制台输出
- VC++中使用内存映射文件处理大文件 (1)
- VC使用Zlib对内存流进行压缩与解压缩
- VC:使用Windows Socket开发应用程序
- VC++中使用内存映射文件处理大文件
- 如何调试编译好的控制台应用程序(VC++)
- VC++中使用内存映射文件处理大文件
- iOS - 代码查看控制台打印内存使用情况:
- VC中使用GDI函数实现位图的透明
- 监控Java应用程序Windows内存使用情况
- VC中使用GDI函数实现位图的透明
- VC使用CRT调试功能来检测内存泄漏
- [转]使用API屏蔽掉控制台应用程序的关闭按钮
- VC++ 6.0 中如何使用 CRT 调试功能来检测内存泄漏
- VC GDI+ 窗口截图内存BMP转JPG,压缩、JPG再转IStream流,IStream流再转 BYTE
- VC++中使用内存映射文件处理大文件
- VC++中使用内存映射文件处理大文件