聚合国内IT技术精华文章,分享IT技术精华,帮助IT从业人士成长

动态字体的贴图管理

2013-04-15 13:01 浏览: 6496 次 我要评论(0 条) 字号:

汉字的显示,是基于 3d api 的图形引擎必须处理的问题。和西方文字不同,汉字的字形很难全部放在一张贴图上,尤其是游戏中有大小不同的字体的需求更是如此。即使放下,也很浪费内存或显存。如果不想申请很大的贴图来存放汉字字形,图形引擎往往需要做动态字形贴图的处理。

即,动态生成一张贴图,把最近常用的汉字画在上面。几乎所有成熟的基于 3d api 的图形引擎都需要有相关的模块才可以对汉字更好的支持。但我到目前为止,还没有看到有把这个模块独立出来的。大多数开源引擎都是在自己的框架内来实现差不多的功能。我觉得,这部分管理功能和如何管理贴图其实没有关系、和取得字形的方法也没有关系、不必和 3d api 打交道,也不用涉及到底用 freetype 还是 os 自带的 api 取得汉字字形,所以值得独立实现。

我们的需求本质上是对一张贴图的区块进行管理。每个汉字都占据其中的一小块。当贴图填满时,最久没有用过的汉字块可以被淘汰掉,让新的汉字覆盖上去。同样的字体的最大高度是相同的,可以排列在一行,但宽度可以不同。横向排列时,少许的空洞的允许的。

我设计了如下接口:

struct dfont_rect {
    int x;
    int y;
    int w;
    int h;
};

struct dfont * dfont_create(int width, int height);
void dfont_release(struct dfont *);
const struct dfont_rect * dfont_lookup(struct dfont *, int c, int height);
const struct dfont_rect * dfont_insert(struct dfont *, int c, int width, int height);
void dfont_flush(struct dfont *);

用 create/release 创建/删除一个管理对象,管理一张指定大小的贴图。贴图本身并不在这个管理对象内,它只负责管理贴图的空间划分。

可以用 insert 来创建一个指定宽高的矩形空间,并为这个空间指定一个 id 。这个 id 一般用汉字的 unicode 即可。insert 会返回一个矩形区域,如果贴图满了,且无法腾出空间,则返回空。禁止创建高度相同的相同 id 。

lookup 可以用于查询一个指定高度的 id ,如果贴图上不存在,则返回空。lookup 会记录这个字最近被使用过,不会被立刻淘汰。

贴图上创建的字型块,在调用 dfont_flush 之前都一定保留。调用 dfont_flush 后,如果贴图满,会淘汰最久没有使用的等高的字形空间。


我把一个开源实现放在 github 上了。不过暂时还没有放在任何项目中用,程序结构略微复杂,所以很可能有 bug 。

具体使用时,每次渲染一个汉字,可以先 lookup 查询过去是否创建过,如果没有,则算出这个字的尺寸,调用 insert 得到一个新的矩形空间,然后取得汉字的位图,上传到贴图的指定区域。每帧渲染完毕,调用一次 dfont_flush 即可。

一般情况下,创建一张 2048 宽的 dfont 对象大约就够用了。但是在字形的淘汰过程中,可能会导致贴图内有空洞(如果能保证尺寸相同,则不会产生这个结果),所以可能会比预想的少排一些字在贴图上。所以实际用的时候,可以考虑创建多创建一个 dfont 对象,交替使用,定期切换。



网友评论已有0条评论, 我也要评论

发表评论

*

* (保密)

Ctrl+Enter 快捷回复