Windows下使用GetGlyphOutline在OpenGL中渲染字体

欢迎转载,请标明出处:http://blog.csdn.net/tianyu2202/

无图无JB,先上图。使用OpenGL绘制字体,支持多种字体,支持TrueType轮廓字体,支持自选字体纹理大小和输出大小,支持在三维空间内绘制。

关于OpenGL中字体的显示网上其实有很多的教程,不过经常用到的方式有比较简单的Bitmap方式、比较复杂的FreeType方式。而本文介绍的方式虽然只能在Windows下实现,却有着和FreeType一样的显示效果,最重要的是非常简单,仅仅200多行代码即可实现。

首先是字体信息的保存,每个文字都有数个信息需要保存,包括了纹理的宽高、起始位置、字体宽度和所在的纹理序号。本文将每个字体保存为一个纹理,而不是常见的一个纹理保存数十个文字。这是首先是为了简单,二来还有应用上的一些特殊性。有这方面要求的同学可以自行优化。

class CFontData
{
public:
	float m_Width, m_Height;
	float m_OrigX, m_OrigY;
	float m_FontWidth;
	GLuint m_TextureID;

	CFontData()
	{
		m_Width = 0.0f;
		m_Height = 0.0f;
		m_TextureID = 0;
		m_FontWidth = 0.0f;
		m_OrigX = 0.0f;
		m_OrigY = 0.0f;
	}

	~CFontData()
	{

	}
};

然后是文字绘制类,非常简单,保存了字体名称、字体大小和一些资源,方法一共两个就是在三维空间内进行绘制和产生纹理字体。

class CFontPrinter
{
private:
	int m_FontSize;
	wchar_t m_FontName[64];
	std::map<wchar_t, CFontData*> m_FontMap;
	HFONT m_Font;

public:
	CFontPrinter(int fontSize, const wchar_t* fontName)
	{
		wcscpy_s(m_FontName, 64, fontName);
		m_FontSize = fontSize;
		m_Font = NULL;
	}

	~CFontPrinter()
	{

	}

	bool makeChar(wchar_t wChar);

	void print3DText(float x, float y, float z, float red, float green, float blue, float height, wchar_t* Text);

	void print3DText(float x, float y, float z, float height, wchar_t* Text)
	{
		return print3DText(x, y, z, 1.0f, 1.0f, 1.0f, height, Text);
	}
};

在makeChar中,根据传入的字符产生纹理并存入m_FontMap,在print3DText中进行绘制。直接使用print3DText也可以自动调用makeChar进行文字数据的生成。首先检查m_Font是否已经生成,没有生成则根据字体信息进行生成。然后使用传入的字符获取Buffer的大小,然后建立Buffer并将它传入函数GetGlyphOutlintW以获取生成的字体数据。而这里生成的数据是Gray64的,每个字节代表一个像素,而且是4字节对齐的。这里我为了节省更多的显卡资源,将PixelStore的对齐设为1字节。使用一个SwapBuffer将数据对齐为1字节并传入TexImage2D。

然后计算所需的数据,存入FontMap中等待绘制。

bool CFontPrinter::makeChar(wchar_t wChar)
{
	HDC hdc = CreateCompatibleDC(wglGetCurrentDC());

	if(!hdc)
		return false;

	HBITMAP hbitmap = CreateCompatibleBitmap(hdc, 1, 1);
	HBITMAP hbitmapOld = (HBITMAP)SelectObject(hdc, hbitmap);

	if((DWORD)hbitmapOld == GDI_ERROR)
		return false;

	CFontData* fontData = new CFontData();

	glGenTextures(1, &fontData->m_TextureID);
	glBindTexture(GL_TEXTURE_2D, fontData->m_TextureID);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
	glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
	glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
	glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
	glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	int iTexWidth = m_FontSize;
	int iTexHeight = m_FontSize;
	int iLevel = 0;

	if (!m_Font)
	{
		// 创建文字
		m_Font = CreateFontW(-m_FontSize, 0, 0, 0, 500, false, false, false,
			DEFAULT_CHARSET, OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS,
			DEFAULT_QUALITY, DEFAULT_PITCH | (m_FontName?FF_DONTCARE:FF_SWISS),	m_FontName);
	}
	if(!m_Font)
		return false;

	HFONT hfontOld = (HFONT)SelectObject(hdc, m_Font);
	if((DWORD)hfontOld == GDI_ERROR)
		return false;

	GLYPHMETRICS gm={0,};
	MAT2 const matrix22_identity={{0,1},{0,0},{0,0},{0,1}};

	DWORD dwBuffSize = GetGlyphOutlineW(hdc, wChar, GGO_GRAY8_BITMAP, &gm, 0, NULL, &matrix22_identity);

	if((signed)dwBuffSize == -1)
	{
		return false;
	}

	if(GetGlyphOutlineW(hdc, wChar, GGO_METRICS, &gm, 0, NULL, &matrix22_identity)==GDI_ERROR)
	{
		return false;
	}

	BYTE *pBuff = new BYTE[dwBuffSize];
	BYTE *pSwapBuff=new BYTE[dwBuffSize];
	memset(pBuff, 0xff, dwBuffSize);
	memset(pSwapBuff, 0xff, dwBuffSize);

	if(GetGlyphOutlineW(hdc, wChar, GGO_GRAY8_BITMAP, &gm, dwBuffSize, pBuff, &matrix22_identity) == GDI_ERROR)
	{
		delete[] pBuff;
		return false;
	}

	if(gm.gmBlackBoxY == 0)
	{
		printf("black box Y zero!!!\n");
		return false;
	}

	// 从 Gray64 转换到 Gray256
	unsigned int const uiRowSize = dwBuffSize / gm.gmBlackBoxY;
	BYTE* pPtr;
	BYTE* pSPtr;
	for(unsigned int nY = 0; nY < gm.gmBlackBoxY; nY++)
	{
		pPtr = pBuff + uiRowSize * nY;
		pSPtr = pSwapBuff + gm.gmBlackBoxX * nY;
		for (unsigned int nX = 0; nX < gm.gmBlackBoxX; nX++)
		{
			if (*pPtr == 0)
			{
				*pSPtr = 0;
			}
			else if (*pPtr == 0x40)
			{
				*pSPtr = 0xff;
			}
			else
			{
				*pSPtr = *pPtr << 2;
			}

			pPtr++;
			pSPtr++;
		}
	}

	// 加载纹理
	glTexImage2D(GL_TEXTURE_2D, iLevel, GL_LUMINANCE8, gm.gmBlackBoxX, gm.gmBlackBoxY, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pSwapBuff);

	// 记录文字信息
	fontData->m_FontWidth = (float)gm.gmCellIncX / (float)m_FontSize;
	fontData->m_Width = (float)gm.gmBlackBoxX / (float)m_FontSize;
	fontData->m_Height = (float)gm.gmBlackBoxY / (float)m_FontSize;
	fontData->m_OrigX = (float)gm.gmptGlyphOrigin.x / (float)m_FontSize;
	fontData->m_OrigY = 1.0f - (float)gm.gmptGlyphOrigin.y / (float)m_FontSize;

	m_FontMap[wChar] = fontData;

	delete[] pBuff;
	delete[] pSwapBuff;

	return true;
}

在Main函数中建立一个OpenGL窗口,并且初始化文字,在渲染循环中调用print3DText即可进行绘制。

void Render()
{
	static float FrameID = 0;

	if (FrameID > 2 * PI)
	{
		FrameID = 0.0f;
	}
	else
	{
		FrameID += PI / 180.0f;
	}

	glDrawBuffer(GL_BACK);
	glViewport(0, 0, m_Width, m_Height);
	glClearColor(0.3f, 0.4f, 0.5f, 1.0f);
	glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	for (int i = 0; i < 20; i++)
	{
		fontPrinter->print3DText(sin(FrameID - PI180 * i) * 0.3f - 0.7f, cos(FrameID - PI180 * i), 0.0f, 1.0f, 1.0f, 1.0f, 0.3f, L"中文输出");
		fontPrinter->print3DText(cos(FrameID - PI180 * i) * 0.4f - 0.8f, sin(FrameID - PI180 * i) * 0.4f, 0.0f, 0.0f, 1.0f, 0.0f, 0.2f, L"Hello Class");
		fontPrinterSong->print3DText(cos(FrameID - PI180 * i) * 0.5f - 0.9f, sin(FrameID - PI180 * i) * 0.6f, 0.0f, 0.0f, 0.0f, 1.0f, 0.4f, L"Font Class");
	}
	fontPrinter->print3DText(-0.7f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.2f, L"Font World");
	fontPrinterSong->print3DText(-0.7f, -0.6f, 0.0f, 1.0f, 0.0f, 0.0f, 0.2f, L"宋体字");
	fontPrinter->print3DText(-0.7f, -0.2f, 0.0f, 1.0f, 0.0f, 0.0f, 0.2f, L"微软雅黑");

	glFlush();

	SwapBuffers(m_hDC);
}

// 初始化文字
void InitFont()
{
	// 参数1 单个文字纹理的最大大小
	// 参数2 字体的文件名称
	fontPrinter = new CFontPrinter(96, L"微软雅黑");
	fontPrinterSong = new CFontPrinter(96, L"宋体");
}

int __stdcall WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in_opt LPSTR lpCmdLine, __in int nShowCmd )
{
	MSG  msg;

	CreateClientWindow();
	InitFont();

	while (true)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))          // 有消息在等待吗?
		{
			if (msg.message == WM_QUIT)      // 收到退出消息?
			{
				// 退出程序
				break;
			}
			else
			{
				TranslateMessage(&msg);                                  // 翻译消息
				DispatchMessage(&msg);                                   // 发送消息
			}
		}
		else
		{
			Render();
		}
	}

	if (fontPrinter != NULL)
	{
		delete fontPrinter;
	}

	return 0;
}

工程代码在 http://download.csdn.net/detail/tianyu2202/7797885 ,欢迎下载。

Windows下使用GetGlyphOutline在OpenGL中渲染字体

时间: 2024-10-07 21:53:37

Windows下使用GetGlyphOutline在OpenGL中渲染字体的相关文章

Golang调用windows下的dll动态库中的函数

Golang调用windows下的dll动态库中的函数 https://blog.csdn.net/xuduorui/article/details/78943331 原文地址:https://www.cnblogs.com/yuanjiangw/p/12196004.html

[笔记]Windows下为mingw安装OpenGL环境(GLUT)

OpenGL只是一个标准,它的实现一般自带在操作系统里,只要确保显卡驱动足够新就可以使用.如果需要在程序里直接使用OpenGL,会有很多非常恶心的预备工作要做.要跳过这些工作,可以用一个utility库,新一点的有GLEW,因为开源所以安装相对方便(大不了丢进去一起编译),但各种教程和书里常见的是闭源的GLUT(很老的库囧),安装相对麻烦,特别是在windows下,折腾了小半天才搞定,所以在这里记录一下. 1. 下载 GLUT 地址在:https://user.xmission.com/~nat

windows下监控vpn状态及中断后自启动

windows服务器vpn远程到内网与内网主机通信,因vpn客户端或其它原因,vpn链接会不定期中断,导致与内网通信中断,中断时不清楚vpn进程是否结束,解决思路如下,前提是客户端启动后会自动拨号链接    判断vpn进程是否存在-->判断与vpn服务器是否为链接状态,若否则启动vpn程序,vpn.bat脚本内容如下    @echo off    关闭回显    c:    切换到vpn客户端所在分区    cd C:\Program Files (x86)\vpn\SSL\vpnClient

Windows下的cmd命令行中设置环境编码

我们都知道,Windows下的cmd命令行默认编码是Windows系统的编码,就是ANSI编码或者说是GBK编码的,这样我们编写的很多应用比如php编写utf-8编码的应用在命令行下面运行时都会出现乱码的情况,原因就是因为运行环境的编码使用自己的编码去解析程序运行结果,而程序本身返回编码和解析编码不一致,最后导致乱码情况的发生. 遇到这种情况,我们可以通过设置cmd命令行环境的编码使编码保持统一来解决,方法如下: 在命令行中,有个命令是chcp,这个命令是用来显示当前活动代码页编号的,也可以理解

Windows下Memcached在.Net程序中的实际运用(从Memcached客户端Enyim的库的编译到实际项目运用) 转发

1.一点基础概念 2.获取EnyimMemcached客户端的源代码并编译出动态库 3.Memcached的服务器安装(windows server) 4.在web项目中实战 一.基础概念 memcached是什么?memcached是分布式缓存系统,特点是高性能.分布式内存缓存系统.memcached能做什么?用来给动态web提升响应速度(通过缓存数据,减少数据库访问压力).为什么要用memcached?笔者认为使用它的原因是能提升网站整体性能,减少数据库的的请求压力.据某位博主说合理使用Me

Windows下Qt开发环境:OpenGL导入3DMax模型(.3DS)

参考:http://blog.csdn.net/cq361106306/article/details/41876541 效果: 源代码: 解释: CLoad3DS.h为加载3DMax模型的头文件,CLoad3DS.cpp为加载3DMax模型的实现文件, nehewidget.h为Qt下使用OpenGL头文件,nehewidget.cpp为Qt下使用OpenGL实现文件. 注意: 1.3D模型和纹理图片资源需要放在源代码同一目录下的Data目录中,即/Data/3DS和/Data/pic下. 2

redis在windows下配置和在.net中的使用

一.什么是redis redis是一个开源的,使用C语言编写的面向键值对类型的分布式Nosql数据库系统,功能类似Memcache,但比Memcache功能更丰富. redis与Memcache的不同点: 支持持久化.主从复制:支持的类型多: Memcache通过客户端驱动实现的集群,存储的时候通过算法来决定把值存在哪一台服务器中. redis做集群与Memcach不同,每一个redis服务器都有一个完整的副本. redis支持的数据类型有5种: string      :   key为stri

Windows下安装Python及Eclipse中配置PyDev插件

最近开始接触Python,鉴于之前安装Java的教训,决定这次边安装Python,边写下历程,供日后反复使用. 在Python官网http://www.python.org/下载Python版本,鉴于目前使用的大多数还是2.X版本和教研室的要求,于是我选择2.7版,下载python-2.7.13.msi.下载完后看看文件大小,一般十几MB,太小说明没有下载完全.由于浏览器原因,我下载之后没有看大小,结果双击打开文件后出现该错误,说明没有下载完全: 下载完成后双击打开,默认C盘,我选择D:\sof

【问】WIndows下C++局部变量在内存中的分布问题

原本是为了看看C++对象模型中子对象赋值给一个父对象和父类型指针指向的域时,到底会不会切割,就打开codebloks写了下面的代码,编译器选的是GNU. 1 #define DEBUG(X) std::cout<<#X"\t=\t"<<X<<std::endl; 2 3 class A 4 { 5 public: 6 int a; 7 A():a(1){} 8 }; 9 10 class B 11 { 12 public: 13 A oa; 14 i