📄 asl_bitmap.cpp
字号:
//-----------------------------------------------------------------------------
//
// ____ 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 + -