您的位置:首页 > 其它

重温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);
	}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: