⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 asl_bitmap.cpp

📁 泡泡堂单机版(含ASL游戏引擎源码 泡泡堂单机版(含ASL游戏引擎源码
💻 CPP
📖 第 1 页 / 共 4 页
字号:
//-----------------------------------------------------------------------------
//
//    ____ Azure Star Game Engine 蓝星游戏引擎 ____
//
//    Copyright (c) 2006, 蓝星工作室
//    All rights reserved.
//
//    文件名称: asl_bitmap.cpp
//    摘    要: 位图类实现
//
//    当前版本: 1.0
//    作    者: 汤  祺
//    创建日期: 2006-7-15
//
//-----------------------------------------------------------------------------

#include "asl_bitmap.h"
#include "asl_asm.h"
#include <stdio.h>
#include <math.h>

namespace ASL
{

//-----------------------------------------------------------------------------
// 函数名: ASLBitmap::ASLBitmap()
// 功  能: 构造函数
// 参  数: [void] - 无
// 返回值: [void] - 无
//-----------------------------------------------------------------------------
ASLBitmap::ASLBitmap(void) :
	m_hBitmap(NULL),
	m_hDC(NULL),
	m_nWidth(0),
	m_nHeight(0),
	m_nPitch(0),
	m_nBlockX(1),
	m_nBlockY(1),
	m_nColorKey(-1),
	m_pData(NULL),
	m_pAlpha(NULL)
{
	m_ptHot.x = 0;
	m_ptHot.y = 0;
}

//-----------------------------------------------------------------------------
// 函数名: ASLBitmap::~ASLBitmap()
// 功  能: 析构函数
// 参  数: [void] - 无
// 返回值: [void] - 无
//-----------------------------------------------------------------------------
ASLBitmap::~ASLBitmap(void)
{
	Destroy();
}

//-----------------------------------------------------------------------------
// 函数名: ASLBitmap::Destroy()
// 功  能: 销毁位图
// 参  数: [void] - 无
// 返回值: [void] - 无
//-----------------------------------------------------------------------------
void ASLBitmap::Destroy(void)
{
	DeleteDC(m_hDC);
	SAFE_DELETE_GDIOBJ(m_hBitmap);
	SAFE_DELETE_ARRAY(m_pAlpha);
	m_nWidth		= 0;
	m_nHeight	= 0;
	m_nPitch		= 0;
	m_nBlockX	= 1;
	m_nBlockY	= 1;
	m_nColorKey	= -1;
	m_ptHot.x		= 0;
	m_ptHot.y		= 0;
	m_pData		= NULL;
	m_hDC		= NULL;
}

//-----------------------------------------------------------------------------
// 函数名: ASLBitmap::CreateBlank()
// 功  能: 创建空白位图
// 参  数: [nWidth] - 位图宽度
//         [nHeight] - 位图高度
// 返回值: [void] - 无
//         创建失败抛出ASLSimpleException类异常
//-----------------------------------------------------------------------------
void ASLBitmap::CreateBlank(int nWidth, int nHeight) throw(ASLSimpleException)
{
	ASSERT(nWidth > 0 && nHeight > 0);

	Destroy();

	// 与位图对应的BITMAPINFO结构
	static int bmpinfo[13] = 	{		sizeof(BITMAPINFOHEADER),		0,	// 宽度		0,	// 高度		0x100001, BI_BITFIELDS, 0, 0, 0, 0, 0,		0xf800, 0x7e0, 0x1f
	};
	bmpinfo[1] = nWidth;
	bmpinfo[2] = -nHeight;

	// 在内存中创建兼容DC
	m_hDC = CreateCompatibleDC(NULL);
	if (m_hDC == NULL)
	{
		throw ASLSimpleException("创建DC失败!");
	}

	// 创建设备无关位图, 同时得到数据区指针, 因而可以自由操纵位图数据
	// 将其选入内存DC后, 又可以当作普通DC使用所有GDI函数
	m_hBitmap = CreateDIBSection(m_hDC, (BITMAPINFO*)bmpinfo, 
		DIB_RGB_COLORS, (void**)&m_pData, NULL, 0);
	if (m_hBitmap == NULL)
	{
		DeleteDC(m_hDC);
		m_hDC = NULL;
		throw ASLSimpleException("创建位图失败!");
	}

	SelectObject(m_hDC, m_hBitmap);					// 将位图选入内存DC
	SelectObject(m_hDC, GetStockObject(NULL_BRUSH));// 设置画刷为透明
	SetBkMode(m_hDC, TRANSPARENT);					// 使文字输出背景透明
	SetStretchBltMode(m_hDC, COLORONCOLOR);			// 使缩放操作能正确执行
	
	m_nBlockW = m_nWidth  = nWidth;
	m_nBlockH = m_nHeight = nHeight;
	m_nPitch  = 2 * (m_nWidth + m_nWidth%2);
	SetRect(&m_rcClip, 0, 0, m_nWidth, m_nHeight);	// 根据位图大小创建裁剪矩形
}

//-----------------------------------------------------------------------------
// 函数名: ASLBitmap::LoadBMP()
// 功  能: 从.bmp文件创建位图(来自内存)
// 参  数: [*pFile] - 文件指针
// 返回值: [void] - 无
//         文件格式错误抛出ASLFileException异常
//-----------------------------------------------------------------------------
void ASLBitmap::LoadBMP(ASLFile *pFile) throw(ASLFileException)
{
	ASSERT(pFile != NULL);

	// 取文件内容指针
	const BYTE *content = pFile->GetPtr();

	// 位图文件头
	const BITMAPFILEHEADER *bmfh = (BITMAPFILEHEADER*)content;

	// 位图信息头
	const BITMAPINFOHEADER *bmih
		= (BITMAPINFOHEADER*)(content + sizeof(BITMAPFILEHEADER));

	// 位图数据区
	const BYTE *data
		= content + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

	// 位图调色板
	const RGBQUAD *quads;

	// 不是位图文件标记
	if (bmfh->bfType != 0x4D42)
	{
		throw ASLFileException(pFile->GetName(), ASLFileException::BadFormat);
	}

	// 若是8位位图, 则引入调色板
	if (bmih->biBitCount == 8)
	{
		int nclrs = bmih->biClrUsed;		
		if (nclrs > 256)
		{
			throw ASLFileException(pFile->GetName(), ASLFileException::BadFormat);
		}
		if (nclrs == 0)
		{
			nclrs = 256;
		}
		quads = (RGBQUAD*)data;
		data += sizeof(RGBQUAD) * nclrs; // 调整数据区位置
	}

	// 创建空白位图
	CreateBlank(bmih->biWidth, abs(bmih->biHeight));

	// 对于不同位深度分别处理, 拷贝到16位的数据区(上下颠倒拷贝)
	// 仅支持常见的8位、24位、32位位图
	int nPitch = (m_nWidth * bmih->biBitCount / 8 + 3) & ~3;	// 每行实际字节数
	int pad = nPitch - m_nWidth * bmih->biBitCount / 8;		// 为对齐数据的空白
	RGBQUAD rgb;
	
	switch (bmih->biBitCount)
	{
	case 8:
		for (int i = m_nHeight-1; i >= 0; --i)
		{
			for (int j = 0; j < m_nWidth; ++j)
			{
				rgb = quads[*data];
				SetPixel(j, i, RGB16(rgb.rgbRed, rgb.rgbGreen, rgb.rgbBlue));
				data++;
			}
			data += pad;
		}
		break;
	case 24:
		for (int i = m_nHeight-1; i >= 0; --i)
		{
			for (int j = 0; j < m_nWidth; ++j)
			{
				SetPixel(j, i, RGB16(data[2], data[1], data[0]));
				data += 3;
			}
			data += pad;
		}
		break;
	case 32:
		m_pAlpha = new BYTE[m_nWidth * m_nHeight + 4];

		for (int i = m_nHeight-1; i >= 0; --i)
		{
			for (int j = 0; j < m_nWidth; ++j)
			{
				SetPixel(j, i, RGB16(data[2], data[1], data[0]));
				m_pAlpha[i * m_nWidth + j] = data[3];
				data += 4;
			}
		}
		break;
	default:
		ASLFileException(pFile->GetName(), ASLFileException::BadFormat);
	}

	// 高度为负说明位图没有上下颠倒, 则将其复原
	if (bmih->biHeight < 0)
	{
		BYTE *buffer = new BYTE[m_nPitch * m_nHeight];
		memcpy(buffer, m_pData, m_nPitch * m_nHeight);
		for (int i = 0; i < m_nHeight; ++i)
		{
			memcpy((BYTE*)m_pData + m_nPitch * (m_nHeight-1-i), 
				   buffer + m_nPitch * i, m_nPitch);
		}
		delete []buffer;

		// 复原nAlpha通道数据
		if (m_pAlpha != NULL)
		{
			BYTE *nAlpha = new BYTE[m_nWidth * m_nHeight];
			memcpy(nAlpha, m_pAlpha, m_nWidth * m_nHeight);
			for (int i = 0; i < m_nHeight; ++i)
			{
				memcpy(m_pAlpha + m_nWidth * (m_nHeight-1-i), 
					nAlpha + m_nWidth * i, m_nWidth);
			}
			delete []nAlpha;
		}
	}	

	// 删除文件
	SAFE_DELETE(pFile);
}

//-----------------------------------------------------------------------------
// 函数名: ASLBitmap::SaveToFile()
// 功  能: 将位图保存为24位.bmp文件
// 参  数: [szFileName] - 要保存的文件名
// 返回值: [void] - 无
//         写文件失败抛出ASLFileException异常
//-----------------------------------------------------------------------------
void ASLBitmap::SaveToFile(LPCSTR szFileName) const throw(ASLFileException)
{
	ASSERT(szFileName != NULL && m_hBitmap != NULL);

	BITMAPFILEHEADER bmfh;						// 位图文件头
	BITMAPINFOHEADER bmih;						// 位图信息头
	FILE *fp = 0;								// 文件指针
	BYTE *buffer = 0;							// 存放24位位图数据
	BYTE *iter = 0;								// 用于位深度转换的游标
	COLOR col;									// 位深度转换中的当前颜色值
	const UINT nPitch = (m_nWidth * 3 + 3) & ~3;	// 24位位图的每行字节数
	const UINT size = nPitch * m_nHeight;			// 24位位图数据区大小
	const UINT pad = nPitch - m_nWidth * 3;		// 24位位图对齐数据的空白

	// 设定位图文件头
	bmfh.bfType				= 0x4D42;
	bmfh.bfSize				= sizeof(bmfh) + sizeof(bmih) + size;
	bmfh.bfReserved1		= 0;
	bmfh.bfReserved2		= 0;
	bmfh.bfOffBits			= sizeof(bmih) + sizeof(bmfh);

	// 设定位图信息头
	bmih.biSize				= sizeof(bmih);
	bmih.biWidth			= m_nWidth;
	bmih.biHeight			= m_nHeight;
	bmih.biPlanes			= 1;
	bmih.biBitCount			= 24;
	bmih.biCompression		= BI_RGB;
	bmih.biSizeImage		= size;
	bmih.biXPelsPerMeter	= 0;
	bmih.biYPelsPerMeter	= 0;
	bmih.biClrUsed			= 0;
	bmih.biClrImportant		= 0;

	// 将16位数据转换为24位数据
	buffer = new BYTE[size];
	ZeroMemory(buffer, size);
	iter = buffer;		
	for (int i = (int)m_nHeight-1; i >= 0; --i)
	{
		for (int j = 0; j < m_nWidth; ++j)
		{
			col = m_pData[i * m_nPitch/2 + j];
			*iter++ = (col & 0x001F) << 3;
			*iter++ = (col & 0x07E0) >> 3;
			*iter++ = (col & 0xF800) >> 8;
		}
		iter += pad;
	}

	// 创建文件并依次写入位图文件头、位图信息头、位图数据
	fp = fopen(szFileName, "wb");
	if (fp == NULL)
	{
		throw ASLFileException(szFileName, ASLFileException::WriteFailed);
	}		
	fwrite(&bmfh, 1, sizeof(BITMAPFILEHEADER), fp);
	fwrite(&bmih, 1, sizeof(BITMAPINFOHEADER), fp);
	fwrite(buffer, sizeof(BYTE), size, fp);
	fclose(fp);
}

//-----------------------------------------------------------------------------
// 函数名: ASLBitmap::BlitToWnd()
// 功  能: 将位图绘制到窗口上
// 参  数: [hwnd] - 窗口句柄
//         [xDest] - 目标点横坐标
//         [yDest] - 目标点纵坐标
//         [*rcSrc] - 源矩形指针, 为空时表示整张位图
// 返回值: [void] - 无
//-----------------------------------------------------------------------------
void ASLBitmap::BlitToWnd(HWND hwnd, int xDest, int yDest, const RECT &rcSrc) const
{
	// 对绘图位置进行调整
	RECT rect;
	if (EqualRect(&rcSrc, &DEFAULTRECT))
	{
		rect = m_rcClip;
	}
	else
	{
		rect = rcSrc;
		if (!_Clip(rect))
		{
			return;
		}
	}

	HDC hdc = ::GetDC(hwnd);

	// 将位图绘制到窗口的DC上
	BitBlt(hdc, xDest, yDest, rect.right - rect.left, rect.bottom - rect.top,
		m_hDC, rect.left, rect.top, SRCCOPY);
	
	::ReleaseDC(hwnd, hdc);
}

//-----------------------------------------------------------------------------
// 函数名: ASLBitmap::BlitToMem565()
// 功  能: 将位图绘制到565格式的内存中, 用于DirectDraw接口
// 参  数: [*ptr] - 内存指针
//         [nPitch] - 目标的每行字节数
// 返回值: [void] - 无
//-----------------------------------------------------------------------------
void ASLBitmap::BlitToMem565(BYTE *pMem, int nPitch) const
{
	// 由于位图本身是按565格式存储的, 所以直接按行拷贝
	for (int i = 0; i < m_nHeight; ++i)
	{
		memcpy(pMem, m_pData + m_nPitch/2 * i, m_nPitch);
		pMem += nPitch;
	}
}

//-----------------------------------------------------------------------------
// 函数名: ASLBitmap::BlitToMem555()
// 功  能: 将位图绘制到555格式的内存中, 用于DirectDraw接口
//          !!该函数未经测试!!
// 参  数: [*ptr] - 内存指针
//         [nPitch] - 目标的每行字节数
// 返回值: [void] - 无
//-----------------------------------------------------------------------------
void ASLBitmap::BlitToMem555(BYTE *pMem, int nPitch) const
{
	BYTE *pdst = pMem;							// 目的指针
	BYTE *psrc = (BYTE*)m_pData;					// 源指针
	const int count = m_nPitch / 8;				// 每行qword的数量
	const __int64 maskh = 0x7FE07FE07FE07FE0;	// 高位掩码, 0111111111100000
	const __int64 maskl = 0x001F001F001F001F;	// 低位掩码, 0000000000011111

	// 将565格式转为555格式, 再拷贝到目的内存中
	for (int i = 0; i < m_nHeight; ++i)
	{
		__asm
		{
			mov esi, psrc		// 源指针给esi
			mov edi, pdst		// 目的指针给edi
			mov ecx, count		// 循环次数给ecx
			movq mm7, maskh		// 高位掩码给mm7
			movq mm6, maskl		// 低位掩码给mm6
			align 4				// 地址对齐
_next:
			movq mm0, [esi]		// 从源地址取一个qword
			movq mm1, mm0		// 拷贝到mm1
			psrlq mm0, 1		// mm0: rrrrrggg gggbbbbb -> 0rrrrrgg ggggbbbb
			pand mm1, mm6		// mm1: rrrrrggg gggbbbbb -> 00000000 000bbbbb
			pand mm0, mm7		// mm0: 0rrrrrgg ggggbbbb -> 0rrrrrgg ggg00000
			add esi, 8			// 源指针加8

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -