📄 asl_font.cpp
字号:
//-----------------------------------------------------------------------------
//
// ____ Azure Star Game Engine 蓝星游戏引擎 ____
//
// Copyright (c) 2006, 蓝星工作室
// All rights reserved.
//
// 文件名称: asl_font.cpp
// 摘 要: 字体类实现
//
// 当前版本: 1.0
// 作 者: 汤 祺
// 创建日期: 2006-8-6
//
//-----------------------------------------------------------------------------
#include "asl_font.h"
#include "asl_asm.h"
namespace ASL
{
//-----------------------------------------------------------------------------
// 函数名: ASLFont::ASLFont()
// 功 能: 构造函数
// 参 数: [void] - 无
// 返回值: [void] - 无
//-----------------------------------------------------------------------------
ASLFont::ASLFont(void)
: m_hDC(NULL)
, m_hBitmap(NULL)
, m_pCache(NULL)
, m_pCharBmpBuf(NULL)
, m_nCharBmpWidth(0)
, m_nCharBmpHeight(0)
, m_nSize(0)
, m_nAscent(0)
, m_nSpace(0)
, m_nTime(0)
, m_bSmooth(false)
{
}
//-----------------------------------------------------------------------------
// 函数名: ASLFont::ASLFont()
// 功 能: 构造函数
// 参 数: [font] - 要创建的字体
// [bSmooth] - 是否平滑字体
// [nCacheSize] - 缓存大小
// 返回值: [void] - 无
//-----------------------------------------------------------------------------
ASLFont::ASLFont(HFONT font, bool bSmooth, int nCacheSize)
{
Create(font, bSmooth, nCacheSize);
}
//-----------------------------------------------------------------------------
// 函数名: ASLFont::~ASLFont()
// 功 能: 析构函数
// 参 数: [void] - 无
// 返回值: [void] - 无
//-----------------------------------------------------------------------------
ASLFont::~ASLFont(void)
{
Destroy();
}
//-----------------------------------------------------------------------------
// 函数名: ASLFont::Destroy()
// 功 能: 销毁字体
// 参 数: [void] - 无
// 返回值: [void] - 无
//-----------------------------------------------------------------------------
void ASLFont::Destroy(void)
{
SAFE_DELETE_ARRAY(m_pCache);
DeleteObject(m_hBitmap);
ReleaseDC(NULL, m_hDC);
m_hDC = NULL;
m_hBitmap = NULL;
m_pCharBmpBuf = NULL;
m_nCharBmpWidth = 0;
m_nCharBmpHeight = 0;
m_nSize = 0;
m_nAscent = 0;
m_nSpace = 0;
m_nTime = 0;
m_bSmooth = false;
}
//-----------------------------------------------------------------------------
// 函数名: ASLFont::Create()
// 功 能: 创建字体
// 参 数: [font] - 要创建的字体
// [bSmooth] - 是否平滑字体
// [nCacheSize] - 缓存大小
// 返回值: [void] - 无
//-----------------------------------------------------------------------------
void ASLFont::Create(HFONT font, bool bSmooth, int nCacheSize)
{
ASSERT(font != NULL && nCacheSize > 0);
// 若已创建过则销毁重新创建
if (m_pCache != NULL)
{
Destroy();
}
m_bSmooth = bSmooth;
m_nSize = nCacheSize;
m_pCache = new CharInfo[m_nSize];
// 创建兼容DC并选入字体
m_hDC = CreateCompatibleDC(NULL);
SelectObject(m_hDC, font);
// 取字体数据
TEXTMETRIC tm;
GetTextMetrics(m_hDC, &tm);
m_nAscent = tm.tmAscent;
m_nCharBmpWidth = (tm.tmMaxCharWidth + 3) & ~3; // 字符位图宽度
m_nCharBmpHeight = tm.tmHeight; // 字符位图高度
m_nAveWidth = tm.tmAveCharWidth;
if (!m_bSmooth)
{
// 与字符位图对应的BITMAPINFO结构
static int bmpinfo[13] = { sizeof(BITMAPINFOHEADER), 0, // 宽度 0, // 高度 0x100001, BI_BITFIELDS, 0, 0, 0, 0, 0, 0xf800, 0x7e0, 0x1f
};
bmpinfo[1] = m_nCharBmpWidth;
bmpinfo[2] = -m_nCharBmpHeight;
// 创建字符位图
m_hBitmap = CreateDIBSection(m_hDC, (BITMAPINFO*)bmpinfo, DIB_RGB_COLORS,
(void**)&m_pCharBmpBuf, 0, 0);
SelectObject(m_hDC, m_hBitmap); // 将字符位图选入DC
SetTextColor(m_hDC, RGB(255, 255, 255)); // 设文字颜色为白色 SetBkColor(m_hDC, RGB(0, 0, 0)); // 设背景颜色为黑色
}
}
//-----------------------------------------------------------------------------
// 函数名: ASLFont::DrawText()
// 功 能: 绘制字符串(格式化字符串)
// 参 数: [&bmDest] - 目标位图
// [x] - 目标x坐标
// [y] - 目标y坐标
// [color] - 文字颜色
// [format] - 格式化字符串
// 返回值: [void] - 无
//-----------------------------------------------------------------------------
void ASLFont::DrawText(ASLBitmap &bmDest, int x, int y, COLOR color,
LPCSTR format, ...)
{
va_list va;
char str[256];
// 处理格式化字符串
va_start(va, format);
vsprintf(str, format, va);
va_end(va);
DrawText(bmDest, x, y, str, color);
}
//-----------------------------------------------------------------------------
// 函数名: ASLFont::DrawText()
// 功 能: 绘制字符串(普通字符串)
// 参 数: [&bmDest] - 目标位图
// [x] - 目标x坐标
// [y] - 目标y坐标
// [str] - 字符串
// [color] - 文字颜色
// 返回值: [void] - 无
//-----------------------------------------------------------------------------
void ASLFont::DrawText(ASLBitmap &bmDest, int x, int y, LPCSTR str, COLOR color)
{
// 越界则直接返回
if ((x > bmDest.GetWidth()) || (y > bmDest.GetHeight()) || (str == NULL))
{
return;
}
UINT c1, c2;
int left = x;
// 依次处理每个字符,计算字符表示数
for (int i = 0; str[i] != '\0'; ++i)
{
c1 = (UCHAR)str[i];
// 若该字符为汉字
if (c1 > 0x80)
{
++i;
c2 = (UCHAR)str[i]; // 取汉字第二各编码
if (c2 == 0) // 若字符串结束
{
break;
}
c1 = c1 * 256 + c2; // 计算汉字的字符表示数
}
const CharInfo &ci = _GetChar(c1); // 取该字符的信息
// 若该字符为可显示字符则显示之
if (c1 > 33)
{
_DrawChar(ci, bmDest, left, y, color);
}
left += ci.ciInc + m_nSpace; // 调整下一字符的绘制点
if (left > bmDest.GetWidth())
{
return;
}
}
}
//-----------------------------------------------------------------------------
// 函数名: ASLFont::DrawTextEx()
// 功 能: 自动分行绘制字符串
// 参 数: [&bmDest] - 目标位图
// [x] - 目标x坐标
// [y] - 目标y坐标
// [str] - 字符串
// [color] - 文字颜色
// [length] - 每行字符数(以char为单位计数)
// [vspace] - 行距
// 返回值: [void] - 无
//-----------------------------------------------------------------------------
void ASLFont::DrawTextEx(ASLBitmap &bmDest, int x, int y, LPCSTR str, COLOR color,
int length, int vspace)
{
ASSERT(str != NULL);
char *buf = new char[strlen(str)];
strcpy(buf, str);
int iter = 0;
// 由于半角全角字可能混合存在, 需要逐字符处理
while (buf[iter] != '\0')
{
// 当前长度已大于等于指定长度, 需要绘制一行
if (iter >= length)
{
// 当前长度大于指定长度, 说明最后一个字是全角字
if (iter > length)
{
iter -= 2; // 退回一个全角字, 保证长度小于等于指定长度
}
// 给本行结尾加结束符, 并绘制这一行
char tmp = buf[iter];
buf[iter] = '\0';
DrawText(bmDest, x, y, color, buf);
buf += iter;
buf[0] = tmp;
iter = 0;
y += vspace;
continue;
}
if (buf[iter] < 0) // 全角字, 加2
{
iter += 2;
}
else // 半角字, 加1
{
iter++;
}
}
// 绘制最后一行
DrawText(bmDest, x, y, color, buf);
SAFE_DELETE_ARRAY(buf);
}
//-----------------------------------------------------------------------------
// 内部函数实现部分
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// 函数名: ASLFont::_GetChar()
// 功 能: 取字符信息
// 本函数使用LRU算法管理字符信息缓存,若缓存命中则直接从缓存中取字
// 符信息,若未命中则调用_SetChar()取字符信息覆盖最久未使用的缓存块
// 参 数: [ch] - 字符表示数
// 返回值: [const ASLFont::CharInfo&] - 字符信息
//-----------------------------------------------------------------------------
const ASLFont::CharInfo& ASLFont::_GetChar(UINT ch)
{
m_nTime++; // 计时器加1
// 计时器溢出则清零
if (m_nTime < 0)
{
m_nTime = 0;
for (int i = 0; i < m_nSize; ++i)
{
m_pCache[i].ciTime = 0;
}
}
// 在Cache中查找
for (int i = 0; i < m_nSize; ++i)
{
if (m_pCache[i].ciChar == ch) // Cache命中
{
m_pCache[i].ciTime = m_nTime; // 设该字符最后使用时间为当前时间
return m_pCache[i];
}
}
// Cache未命中,查找Cache中最久未被使用的字(ciTime最小)
int min = m_pCache[m_nSize-1].ciTime;
int pos = m_nSize-1;
for (int i = m_nSize-1; i >= 0; --i)
{
if (m_pCache[i].ciTime < min)
{
min = m_pCache[i].ciTime;
pos = i;
}
}
_SetChar(m_pCache[pos], ch); // 替换最久未被使用的字
m_pCache[pos].ciTime = m_nTime; // 设该字符最后使用时间为当前时间
return m_pCache[pos];
}
//-----------------------------------------------------------------------------
// 函数名: ASLFont::_SetChar()
// 功 能: 设置字符信息
// 参 数: [&ci] - 待填字符信息结构
// [ch] - 字符表示数
// 返回值: [void] - 无
//-----------------------------------------------------------------------------
void ASLFont::_SetChar(CharInfo &ci, UINT ch)
{
if (m_bSmooth)
{
int size; static MAT2 mat2={{0,1},{0,0},{0,0},{0,1}}; GLYPHMETRICS gm; // 取存放字符Alpha数据所需的缓冲区大小 size = GetGlyphOutline(m_hDC, ch, GGO_GRAY8_BITMAP, &gm, 0, NULL, &mat2); // 取缓冲区大小失败,最可能原因为m_hDC中没有选入字体 if (size <= 0) { //exception } // 申请缓冲区(存放字符Alpha数据) SAFE_DELETE_ARRAY(ci.ciData); ci.ciData = new BYTE[size]; // 取字符Alpha数据及其他辅助数据 if (GetGlyphOutline(m_hDC, ch, GGO_GRAY8_BITMAP, &gm, size, (LPVOID)ci.ciData, &mat2) == GDI_ERROR) { //exception }
// 填入其他字符信息
ci.ciChar = ch;
ci.ciWidth = gm.gmBlackBoxX;
ci.ciHeight = gm.gmBlackBoxY;
ci.ciPitch = (ci.ciWidth + 3) & 0xFFFFFFFC;
ci.ciOx = gm.gmptGlyphOrigin.x;
ci.ciOy = m_nAscent - gm.gmptGlyphOrigin.y;
ci.ciInc = gm.gmCellIncX;
}
else
{
static char str[3] = { 0, 0, 0 }; SIZE size;
// 将字符表示数转换为字符串,输出到内存中
if (ch < 256) { str[0] = (char)ch; str[1] = '\0'; ::TextOut(m_hDC, 0, 0, str, 1); ::GetTextExtentPoint(m_hDC, str, 1, &size); } else { str[0] = ch >> 8; str[1] = ch & 0xFF; ::TextOut(m_hDC, 0, 0, str, 2); ::GetTextExtentPoint(m_hDC, str, 2, &size); }
// 调整文字边界,确保不出界
if (size.cx > m_nCharBmpWidth)
{
size.cx = m_nCharBmpWidth;
}
if (size.cy > m_nCharBmpHeight)
{
size.cy = m_nCharBmpHeight;
}
// 从内存中把16位数据转为8位数据并保存到字符信息中
ci.ciData = new BYTE[size.cx * size.cy + 4]; // 加4是便于汇编处理
BYTE *pdst = ci.ciData;
WORD *psrc = (WORD*)m_pCharBmpBuf;
for (int i = 0; i < size.cy; ++i)
{
for (int j = 0; j < size.cx; ++j)
{
pdst[j] = psrc[j] == 0 ? 0 : 0xFF;
}
pdst += size.cx;
psrc += m_nCharBmpWidth;
}
// 填入其他字符信息
ci.ciChar = ch;
ci.ciWidth = size.cx;
ci.ciHeight = size.cy;
ci.ciPitch = size.cx;
ci.ciOx = 0;
ci.ciOy = 0;
ci.ciInc = size.cx;
}
}
//-----------------------------------------------------------------------------
// 函数名: ASLFont::_DrawChar()
// 功 能: 绘制字符
// 参 数: [&ci] - 字符信息
// [&bmDest] - 目标位图
// [x] - 目标x点
// [y] - 目标y点
// [color] - 字符颜色
// 返回值: [void] - 无
//-----------------------------------------------------------------------------
void ASLFont::_DrawChar(const CharInfo &ci, ASLBitmap &bmDest, int x, int y,
COLOR color) const
{
ASSERT(ci.ciData != NULL);
RECT rect;
SetRect(&rect, 0, 0, ci.ciWidth, ci.ciHeight);
// 调整绘图位置
x += ci.ciOx;
y += ci.ciOy;
if (x < 0)
{
rect.left -= x;
x = 0;
}
if (y < 0)
{
rect.top -= y;
y = 0;
}
if (x + ci.ciWidth > bmDest.GetWidth())
{
rect.right -= x + ci.ciWidth - bmDest.GetWidth();
}
if (y + ci.ciHeight > bmDest.GetHeight())
{
rect.bottom -= y + ci.ciHeight - bmDest.GetHeight();
}
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
// 若符合绘制条件
if (x < bmDest.GetWidth() && y < bmDest.GetHeight() && width > 0 && height > 0)
{
// 目的指针
BYTE *pdst = (BYTE*)bmDest.GetData() + y * bmDest.GetPitch() + x * 2;
// 字体数据通道指针
BYTE *psrc = ci.ciData + rect.top * ci.ciPitch + rect.left;
// 64位颜色值
__int64 clr64 = Bit16To64(color);
// 根据是否平滑绘制选择不同的汇编函数
void (*DrawFunc)(BYTE*, BYTE*, int, __int64);
DrawFunc = m_bSmooth ? asmSmoothFont : asmPlainFont;
// 逐行处理
for (int i = 0; i < height; ++i)
{
DrawFunc(psrc, pdst, width, clr64);
psrc += ci.ciPitch;
pdst += bmDest.GetPitch();
}
}
}
} // namespace ASL
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -