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

📄 asl_font.cpp

📁 泡泡堂单机版(含ASL游戏引擎源码 泡泡堂单机版(含ASL游戏引擎源码
💻 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 + -