重温WIN32 API ------ 窗口上绘制位图文件
2014-12-22 18:49
363 查看
1 基本思路
做界面仿真时,经常需要在窗口上贴图,随着图片数量的增多,通过资源文件中添加位图的方式会变得越来越不可控。所以本着“资源与程序分离“的原则,还是使用外部位图文件更加清晰明了。那么如何管理位图的贴图位置呢?如果写死到程序代码中,则又会导致耦合行过高的问题。容易想到解决方法有两个,一是使用一个单独的xml文件来记录图片文件名称和贴图位置的关系,二是直接把贴图位置信息包含进位图文件的文件名中。本文采用更加简单的后者,位图文件名格式规范为:description_xxx-yyy.bmp ,其中xxx为贴图时相对于所在窗口的x坐标,yyy为贴图时相对于所在窗口的y坐标,例如:发动机按钮_100-200.bmp,表示贴图时,目的坐标点为(100,200)。
至于贴图的实现,考虑到只需要支持bmp一种格式即可,所以采用GDI库完成。考虑到一个位图文件可能会被贴图多次(例如刷新的时候),所以实现时没有直接SetDIBitsToDevice(),而是首先把DIB通过CreateDIBitmap()转化为DDB,然后保存这个DDB,这样以后每次贴图时,只需要BitBlt()这个DDB就可以了,提高了效率。
2 代码实现
BitmapHelper.h#pragma once /******************************************************************************** BitmapHelper 贴图助手 功能描述: 根据位图文件名,把位图文件读入并贴到指定窗口,为提高效率对象内部一直 保存读入内存的BITMAP,所以只在第一次贴图时需要从外部文件读取。 使用说明: 每一个位图文件对应一个BitmapHelper类对象。使用样例: BitmapHelper *pBmp = new BitmapHelper(L"D:\\风景_100-300.bmp"); pBmp->ShowOnWindow(this->m_hWnd); delete pBmp; // 程序结束或不再需要这个位图时,删除 ********************************************************************************/ class BitmapHelper { public: BitmapHelper(TCHAR* file); ~BitmapHelper(); protected: TCHAR fileName[256] ; // 位图文件名 HBITMAP hBitmap; // 位图句柄 int desX; // 目的x坐标 int desY; // 目的y坐标 protected: void ShowOnWindow(HWND hwnd, int x, int y); // 在指定窗口上显示 public: HBITMAP CreateBitmapObjectFromDibFile(HDC hdc); //从文件中获取DDB void ShowOnWindow(HWND hwnd); void ShowOnDevice(HDC dc, int x, int y); };
BitmapHelper.cpp
#include "stdafx.h" #include "BitmapHelper.h" #include "string.h" BitmapHelper::BitmapHelper(TCHAR* file) { this->hBitmap = NULL; this->desX = -9999; this->desY = -9999; ::StrCpyNW(this->fileName, file, 256); this->fileName[255] = TEXT('\0'); } /* 功能:从位图文件创建DDB 参数: hdc 设备DC 返回值: DDB句柄,错误返回NULL */ HBITMAP BitmapHelper::CreateBitmapObjectFromDibFile(HDC hdc) { BITMAPFILEHEADER* pbmfh = NULL; HANDLE hFile = NULL; DWORD dwFileSize = 0; hFile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (hFile == INVALID_HANDLE_VALUE) { return NULL; } dwFileSize = GetFileSize(hFile, NULL); pbmfh = (BITMAPFILEHEADER*)malloc(dwFileSize); if (pbmfh == NULL) { CloseHandle(hFile); return NULL; } DWORD dwBytesRead = 0; BOOL bSuccess = ::ReadFile(hFile, pbmfh, dwFileSize, &dwBytesRead, NULL); ::CloseHandle(hFile); // 验证文件确实是位图文件 if (!bSuccess || dwBytesRead != dwFileSize || pbmfh->bfType != *(WORD*) "BM" || pbmfh->bfSize != dwFileSize) { free(pbmfh); return NULL; } HBITMAP hBitmap = ::CreateDIBitmap(hdc, (BITMAPINFOHEADER*)(pbmfh + 1), CBM_INIT, (BYTE*)pbmfh + pbmfh->bfOffBits, (BITMAPINFO*)(pbmfh + 1), DIB_RGB_COLORS); free(pbmfh); return hBitmap; } /* 功能:在指定的设备上显示图片 参数:hdc 设备DC, (x,y)为目的左上角坐标 */ void BitmapHelper::ShowOnDevice(HDC hdc, int x, int y) { if (this->hBitmap == NULL) { this->hBitmap = this->CreateBitmapObjectFromDibFile(hdc); } if (this->hBitmap != NULL) { BITMAP bitmap; ::GetObject(this->hBitmap, sizeof(BITMAP), &bitmap); HDC hdcMem = ::CreateCompatibleDC(hdc); ::SelectObject(hdcMem, this->hBitmap); ::BitBlt(hdc, x, y, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY); ::DeleteDC(hdcMem); } } /* 功能:在指定窗口上显示位图 参数:hwnd 窗口句柄, (x,y) 目的左上角坐标 */ void BitmapHelper::ShowOnWindow(HWND hwnd, int x, int y) { if (this->hBitmap == NULL) { HDC hdc = ::GetDC(hwnd); this->hBitmap = this->CreateBitmapObjectFromDibFile(hdc); ::ReleaseDC(hwnd, hdc); } if (this->hBitmap != NULL) { BITMAP bitmap; ::GetObject(this->hBitmap, sizeof(BITMAP), &bitmap); HDC hdc = ::GetDC(hwnd); HDC hdcMem = ::CreateCompatibleDC(hdc); ::SelectObject(hdcMem, this->hBitmap); ::BitBlt(hdc, x, y, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY); ::DeleteDC(hdcMem); ::ReleaseDC(hwnd, hdc); } } /* 功能:根据文件名解析位置显示位图文件到指定窗口 参数: hwnd 目的窗口 说明: 位图文件命名规范 name_xxx-yyy.bmp */ void BitmapHelper::ShowOnWindow(HWND hwnd) { // 解析文件名 if (this->desX == -9999) { int i = 0; int indexLast_ = 0; //最后一个_,表示坐标的开始 int indexLastDot = 0; //最后一个.,表示扩展名的开始 int indexSep = 0; // 坐标分割标志- int n = wcslen(this->fileName); for (i = n-1; i >=0; i--) { if (this->fileName[i] == L'_') { break; } } indexLast_ = (i == 0 ? -1 : i); // -1 表示没有目录部分 for (i = n - 1; i >= 0; i--) { if (this->fileName[i] == L'.') { break; } } indexLastDot = (i == 0 ? n : i); // n表示没有扩展名部分 int xyStart = indexLast_ + 1; // 坐标起始位置 int xyEnd = indexLastDot - 1; // 坐标结束位置 for (i = xyStart; i <= xyEnd; i++) { if (this->fileName[i] == L'-') { break; } } indexSep = i == xyEnd ? -1 : i; // -1 表示没有找到- if (n==0 || indexSep==-1) { this->desX = 0; this->desY = 0; } else { TCHAR s_x[10]; int count = indexSep - xyStart; wcsncpy_s(s_x, 10, this->fileName+xyStart, count); s_x[count] = L'\0'; this->desX = _wtoi(s_x); WCHAR s_y[10]; count = xyEnd - indexSep; wcsncpy_s(s_y, 10, this->fileName+indexSep+1, count); s_y[count] = L'\0'; this->desY = _wtoi(s_y); } } this->ShowOnWindow(hwnd, this->desX, this->desY); } BitmapHelper::~BitmapHelper() { if (this->hBitmap != NULL) // 清理位图资源 { ::DeleteObject(this->hBitmap); } }
相关文章推荐
- 重温WIN32 API ------ SetTimer()无窗口调用方式
- 重温 Win32 API ----- 截屏指定窗口并打印
- 重温WIN32 API ------ 最简单的Windows窗口封装类
- vba使用win32 API(GetOpenFileName )实现打开文件对话框
- vba使用win32 API(GetOpenFileName )实现打开文件对话框
- vba使用win32 API(GetOpenFileName )实现打开文件对话框
- vba使用win32 API(GetOpenFileName )实现打开文件对话框
- 用Win32 API创建不规则窗口
- 窗口启动特效,使用win32 api
- vba使用win32 API(GetOpenFileName )实现打开文件对话框
- vba使用win32 API(GetOpenFileName )实现打开文件对话框
- 位图绘制后拖放窗口时闪动现象的解决方案
- VB之Win32 API编程--判断本地或远程文件是否存在的简易方法
- 用Win32 API创建不规则窗口
- vba使用win32 API(GetOpenFileName )实现打开文件对话框
- vba使用win32 API(GetOpenFileName )实现打开文件对话框
- dotnet调用win32 api实现文件搜索
- win32 API窗口句柄的获得--我的经验
- vba使用win32 API(GetOpenFileName )实现打开文件对话框
- vba使用win32 API(GetOpenFileName )实现打开文件对话框