动态字体的贴图管理
汉字的显示,是基于 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 对象,交替使用,定期切换。
Comments
Posted by: zhupf | (20) August 4, 2013 12:34 AM
Posted by: 林翌蕁 | (19) May 15, 2013 11:50 PM
Posted by: 林翌蕁 | (18) May 15, 2013 11:17 PM
Posted by: Cloud | (17) April 8, 2013 03:00 PM
Posted by: davidxu | (16) April 7, 2013 03:11 PM
Posted by: Cloud | (15) April 4, 2013 10:36 AM
Posted by: tom | (14) April 4, 2013 10:18 AM
Posted by: 秒大刀 | (13) April 3, 2013 04:23 PM
Posted by: 牛顿顿 | (12) April 3, 2013 02:56 PM
Posted by: yuanlin2008 | (11) April 3, 2013 11:03 AM
Posted by: 韦恩 | (10) April 3, 2013 10:37 AM
Posted by: i4oolish | (9) April 2, 2013 10:12 PM
Posted by: wei | (8) April 2, 2013 08:48 PM
Posted by: readdir_r | (7) April 2, 2013 08:38 PM
Posted by: fish | (6) April 2, 2013 07:25 PM
Posted by: et | (5) April 2, 2013 06:40 PM
Posted by: Anonymous | (4) April 2, 2013 06:22 PM
Posted by: marmot | (3) April 2, 2013 06:15 PM
Posted by: Cloud | (2) April 2, 2013 05:14 PM
Posted by: 匿名 | (1) April 2, 2013 05:03 PM