LOG.ZS.0001.基于Freetype的游戏字体渲染优化思路

 

Total

Utf8-ucs2

Html_parse

Layout

Render_string

Init_texture

Ft_load_glyph

原始

2293

1

26

708

1556

2

1403

 

 

 

 

 

 

 

 

 

上表用于记录优化各步骤的消耗时间。

生成的文本纹理,文本是加州宾馆的全歌词。

原始版本整个纹理的生成耗时2300毫秒左右,可以看到实际是慢得发指。

究其原因,是由于实现iron引擎的时候未进行任何性能方面的考虑。

上述时间表的构成:

total : 总消耗时间

utf8-ucs2: 将utf8字符串转换为ucs2的时间

html_parse : 引擎需要支持一定程度的富文本,所以需要支持简单的html解析.

render_string: 将字符串转换为纹理所消耗的总时间

Ft_load_glygh : 在整个过程中, 调用Ft_load_glygh 消耗的总时间。

 

由以上分析可知,最消耗时间的调用是Ft_load_glygh。

尚不知道为什么Ft_load_glygh消耗的时间这么大,原以为是因为每次Ft_load_glygh会到文件中去查询加载具体字符数据,而将程序改造成从内存加载字库数据,仍然耗时巨大。

如果freetype本身无法优化,那么就需要从其他方向来考虑这个问题。

 

首先重新阐述一下字体渲染的需求:

(1) 常规的字体属性需要支持,如font_size

(2) 能够为字体指定边框颜色,边框尺寸,填充颜色。

(3) 能够按单行,或者多行进行布局。

(4) 能够一定程度支持富文本, 例如为精灵设置text = <font style="color:#FF0000; ">某X</font>施展了<font style=" color:#FF0000;outline:1;outline_color:#00FFFF">愤怒一击</font>

(5) 性能不需要达到多高,但至少做到切换场景,不能因为字体纹理生成而产生明显卡顿(特别是有动态字体的时候,比如聊天).

 

综上所属,一次纹理生成的过程至少包含如下逻辑

--> 将utf8转换为ucs2

--> 解析富文本

--> 计算布局

--> 生成纹理

 

这里需要说明的是: 按上表所述, Layout 消耗掉708ms的原因是,Layout需要获取各字符的尺寸. 所以调用了FT_Load_Glyph, 该函数起码就占用了700ms。

之后在生成纹理过程中,对FT_Load_Glyph再次进行调用,所以不慢都不行。

 

问题大致清楚后,考虑解决方案.

目前初步考虑的优化方案是:避免每次生成字符纹理,都去调用Ft_load_glyph加载字模数据,改为采用缓存机制.仅在第一次加载字体时,才会调用Ft_load_glyph。之后的调用,会从缓存中读取。 至于是不是要对缓存设置阀值,控制内存增长,这里先暂时不考虑。

那么接下来就需要确定我到底应该缓存什么。

缓存什么呢,缓存什么呢。

先把参考用的freetype库,生成带边框字体的example代码贴上。

http://www.freetype.org/freetype2/docs/tutorial/example2.cpp

这个范例代码写得丑陋,但是逻辑还是很简单的。

大体上的过程实际就是加载字模后,

获取字模fill 部分的Span表,

获取字模outline 部分的Span表,

将两部分数据打到一个Bitmap中.

这个Span结构需要特别说明一下,很有意思。

struct Span



{



  Span() { }



  Span(int _x, int _y, int _width, int _coverage)



  : x(_x), y(_y), width(_width), coverage(_coverage) { }



 



  int x, y, width, coverage;



};

  

 

他实际描述的是一条横向扫描线,起点在(x, y), 宽度为with,  converage 存储的是灰度数据(0-255)。你后面需要做的是拿到这个表以后,进行遍历,将Span数据外加外部输入的颜色值(边框颜色 和 填充颜色) 转换成最终的Bitmap。

 

由于我们的字体支持包边,所以我们可能会拿到两个Span表,一个是用于渲染outline的,姑且叫他outlineSpans, 另一个是用于渲染填充区的,姑且叫他fillSpans。

实际上,outlineSpan 和 fillSpan 所需要的Bitmap尺寸是不同的,通常是边框需要的Bitmap略大于填充区所需要的Bitmap。

Freetype的设计,由于是一次Ft_load_glyp获得outlineSpans 和 fillSpans, 所以可以猜想,他们的坐标系统是统一的,也就是说原点(0,0)点是重合的, 这也是为什么x, y 有时会出现负数的原因。

这个span啥的有个好处,实际上有了这个span, 基本上就可以脱离freetype的接口自己画文字了。当然他应该还有更raw的东西,目前没考虑要接触。

 

为了证实猜想,我首先做了一个测试。

打印出字符'g'的outlineSpans 和 fillSpans的数据.

其结果显示如下:

[2.24 19:55:11]      [INFO]   there are 94 span in fill rect.



[2.24 19:55:11]      [INFO]   [0] x:2 y:-5 width:1 gray8:38



[2.24 19:55:11]      [INFO]   [1] x:3 y:-5 width:1 gray8:163



[2.24 19:55:11]      [INFO]   [2] x:4 y:-5 width:1 gray8:232



[2.24 19:55:11]      [INFO]   [3] x:5 y:-5 width:1 gray8:251



[2.24 19:55:11]      [INFO]   [4] x:6 y:-5 width:1 gray8:232



[2.24 19:55:11]      [INFO]   [5] x:7 y:-5 width:1 gray8:179



[2.24 19:55:11]      [INFO]   [6] x:8 y:-5 width:1 gray8:74



[2.24 19:55:11]      [INFO]   [7] x:2 y:-4 width:1 gray8:245



[2.24 19:55:11]      [INFO]   [8] x:3 y:-4 width:6 gray8:255



[2.24 19:55:11]      [INFO]   [9] x:9 y:-4 width:1 gray8:134



[2.24 19:55:11]      [INFO]   [10] x:2 y:-3 width:1 gray8:225



[2.24 19:55:11]      [INFO]   [11] x:3 y:-3 width:1 gray8:103



[2.24 19:55:11]      [INFO]   [12] x:4 y:-3 width:1 gray8:25



[2.24 19:55:11]      [INFO]   [13] x:5 y:-3 width:1 gray8:6



[2.24 19:55:11]      [INFO]   [14] x:6 y:-3 width:1 gray8:42



[2.24 19:55:11]      [INFO]   [15] x:7 y:-3 width:1 gray8:158



[2.24 19:55:11]      [INFO]   [16] x:8 y:-3 width:2 gray8:255



[2.24 19:55:11]      [INFO]   [17] x:10 y:-3 width:1 gray8:63



[2.24 19:55:11]      [INFO]   [18] x:2 y:-2 width:1 gray8:12



[2.24 19:55:11]      [INFO]   [19] x:8 y:-2 width:1 gray8:168



[2.24 19:55:11]      [INFO]   [20] x:9 y:-2 width:1 gray8:255



[2.24 19:55:11]      [INFO]   [21] x:10 y:-2 width:1 gray8:167



[2.24 19:55:11]      [INFO]   [22] x:8 y:-1 width:1 gray8:59



[2.24 19:55:11]      [INFO]   [23] x:9 y:-1 width:1 gray8:255



[2.24 19:55:11]      [INFO]   [24] x:10 y:-1 width:1 gray8:223



[2.24 19:55:11]      [INFO]   [25] x:2 y:0 width:1 gray8:16



[2.24 19:55:11]      [INFO]   [26] x:3 y:0 width:1 gray8:147



[2.24 19:55:11]      [INFO]   [27] x:4 y:0 width:1 gray8:230



[2.24 19:55:11]      [INFO]   [28] x:5 y:0 width:1 gray8:247



[2.24 19:55:11]      [INFO]   [29] x:6 y:0 width:1 gray8:206



[2.24 19:55:11]      [INFO]   [30] x:7 y:0 width:1 gray8:80



[2.24 19:55:11]      [INFO]   [31] x:8 y:0 width:1 gray8:13



[2.24 19:55:11]      [INFO]   [32] x:9 y:0 width:1 gray8:255



[2.24 19:55:11]      [INFO]   [33] x:10 y:0 width:1 gray8:247



[2.24 19:55:11]      [INFO]   [34] x:1 y:1 width:1 gray8:8



[2.24 19:55:11]      [INFO]   [35] x:2 y:1 width:1 gray8:207



[2.24 19:55:11]      [INFO]   [36] x:3 y:1 width:4 gray8:255



[2.24 19:55:11]      [INFO]   [37] x:7 y:1 width:1 gray8:254



[2.24 19:55:11]      [INFO]   [38] x:8 y:1 width:1 gray8:88



[2.24 19:55:11]      [INFO]   [39] x:9 y:1 width:2 gray8:255



[2.24 19:55:11]      [INFO]   [40] x:11 y:1 width:0 gray8:248



[2.24 19:55:11]      [INFO]   [41] x:1 y:2 width:1 gray8:119



[2.24 19:55:11]      [INFO]   [42] x:2 y:2 width:1 gray8:255



[2.24 19:55:11]      [INFO]   [43] x:3 y:2 width:1 gray8:247



[2.24 19:55:11]      [INFO]   [44] x:4 y:2 width:1 gray8:108



[2.24 19:55:11]      [INFO]   [45] x:5 y:2 width:1 gray8:21



[2.24 19:55:11]      [INFO]   [46] x:6 y:2 width:1 gray8:24



[2.24 19:55:11]      [INFO]   [47] x:7 y:2 width:1 gray8:113



[2.24 19:55:11]      [INFO]   [48] x:8 y:2 width:1 gray8:227



[2.24 19:55:11]      [INFO]   [49] x:9 y:2 width:2 gray8:255



[2.24 19:55:11]      [INFO]   [50] x:1 y:3 width:1 gray8:205



[2.24 19:55:11]      [INFO]   [51] x:2 y:3 width:1 gray8:255



[2.24 19:55:11]      [INFO]   [52] x:3 y:3 width:1 gray8:120



[2.24 19:55:11]      [INFO]   [53] x:8 y:3 width:1 gray8:114



[2.24 19:55:11]      [INFO]   [54] x:9 y:3 width:2 gray8:255



[2.24 19:55:11]      [INFO]   [55] x:1 y:4 width:1 gray8:244



[2.24 19:55:11]      [INFO]   [56] x:2 y:4 width:1 gray8:255



[2.24 19:55:11]      [INFO]   [57] x:3 y:4 width:1 gray8:31



[2.24 19:55:11]      [INFO]   [58] x:8 y:4 width:1 gray8:25



[2.24 19:55:11]      [INFO]   [59] x:9 y:4 width:2 gray8:255



[2.24 19:55:11]      [INFO]   [60] x:1 y:5 width:1 gray8:250



[2.24 19:55:11]      [INFO]   [61] x:2 y:5 width:1 gray8:255



[2.24 19:55:11]      [INFO]   [62] x:3 y:5 width:1 gray8:9



[2.24 19:55:11]      [INFO]   [63] x:9 y:5 width:2 gray8:255



[2.24 19:55:11]      [INFO]   [64] x:1 y:6 width:1 gray8:228



[2.24 19:55:11]      [INFO]   [65] x:2 y:6 width:1 gray8:255



[2.24 19:55:11]      [INFO]   [66] x:3 y:6 width:1 gray8:41



[2.24 19:55:11]      [INFO]   [67] x:8 y:6 width:1 gray8:10



[2.24 19:55:11]      [INFO]   [68] x:9 y:6 width:2 gray8:255



[2.24 19:55:11]      [INFO]   [69] x:1 y:7 width:1 gray8:172



[2.24 19:55:11]      [INFO]   [70] x:2 y:7 width:1 gray8:255



[2.24 19:55:11]      [INFO]   [71] x:3 y:7 width:1 gray8:141



[2.24 19:55:11]      [INFO]   [72] x:8 y:7 width:1 gray8:83



[2.24 19:55:11]      [INFO]   [73] x:9 y:7 width:2 gray8:255



[2.24 19:55:11]      [INFO]   [74] x:1 y:8 width:1 gray8:68



[2.24 19:55:11]      [INFO]   [75] x:2 y:8 width:1 gray8:255



[2.24 19:55:11]      [INFO]   [76] x:3 y:8 width:1 gray8:252



[2.24 19:55:11]      [INFO]   [77] x:4 y:8 width:1 gray8:128



[2.24 19:55:11]      [INFO]   [78] x:5 y:8 width:1 gray8:32



[2.24 19:55:11]      [INFO]   [79] x:6 y:8 width:1 gray8:16



[2.24 19:55:11]      [INFO]   [80] x:7 y:8 width:1 gray8:91



[2.24 19:55:11]      [INFO]   [81] x:8 y:8 width:1 gray8:235



[2.24 19:55:11]      [INFO]   [82] x:9 y:8 width:2 gray8:255



[2.24 19:55:11]      [INFO]   [83] x:2 y:9 width:1 gray8:147



[2.24 19:55:11]      [INFO]   [84] x:3 y:9 width:5 gray8:255



[2.24 19:55:11]      [INFO]   [85] x:8 y:9 width:1 gray8:142



[2.24 19:55:11]      [INFO]   [86] x:9 y:9 width:2 gray8:255



[2.24 19:55:11]      [INFO]   [87] x:3 y:10 width:1 gray8:100



[2.24 19:55:11]      [INFO]   [88] x:4 y:10 width:1 gray8:205



[2.24 19:55:11]      [INFO]   [89] x:5 y:10 width:1 gray8:246



[2.24 19:55:11]      [INFO]   [90] x:6 y:10 width:1 gray8:231



[2.24 19:55:11]      [INFO]   [91] x:7 y:10 width:1 gray8:132



[2.24 19:55:11]      [INFO]   [92] x:8 y:10 width:1 gray8:3



[2.24 19:55:11]      [INFO]   [93] x:9 y:10 width:2 gray8:255



[2.24 19:55:11]      [INFO]   fill rect :(0.000000,-5.000000) - (11.000000,10.000000), w:12.000000, h:16.000000



[2.24 19:55:11]      [INFO]   ---------------------------------------------------



[2.24 19:55:11]      [INFO]   there are 91 span in outline rect.



[2.24 19:55:11]      [INFO]   [0] x:2 y:-6 width:1 gray8:70



[2.24 19:55:11]      [INFO]   [1] x:3 y:-6 width:1 gray8:178



[2.24 19:55:11]      [INFO]   [2] x:4 y:-6 width:1 gray8:235



[2.24 19:55:11]      [INFO]   [3] x:5 y:-6 width:1 gray8:250



[2.24 19:55:11]      [INFO]   [4] x:6 y:-6 width:1 gray8:233



[2.24 19:55:11]      [INFO]   [5] x:7 y:-6 width:1 gray8:185



[2.24 19:55:11]      [INFO]   [6] x:8 y:-6 width:1 gray8:97



[2.24 19:55:11]      [INFO]   [7] x:9 y:-6 width:1 gray8:5



[2.24 19:55:11]      [INFO]   [8] x:1 y:-5 width:1 gray8:139



[2.24 19:55:11]      [INFO]   [9] x:2 y:-5 width:7 gray8:255



[2.24 19:55:11]      [INFO]   [10] x:9 y:-5 width:1 gray8:207



[2.24 19:55:11]      [INFO]   [11] x:10 y:-5 width:1 gray8:28



[2.24 19:55:11]      [INFO]   [12] x:1 y:-4 width:1 gray8:252



[2.24 19:55:11]      [INFO]   [13] x:2 y:-4 width:8 gray8:255



[2.24 19:55:11]      [INFO]   [14] x:10 y:-4 width:1 gray8:202



[2.24 19:55:11]      [INFO]   [15] x:11 y:-4 width:1 gray8:4



[2.24 19:55:11]      [INFO]   [16] x:1 y:-3 width:10 gray8:255



[2.24 19:55:11]      [INFO]   [17] x:11 y:-3 width:1 gray8:86



[2.24 19:55:11]      [INFO]   [18] x:1 y:-2 width:1 gray8:225



[2.24 19:55:11]      [INFO]   [19] x:2 y:-2 width:1 gray8:246



[2.24 19:55:11]      [INFO]   [20] x:3 y:-2 width:1 gray8:128



[2.24 19:55:11]      [INFO]   [21] x:4 y:-2 width:1 gray8:30



[2.24 19:55:11]      [INFO]   [22] x:5 y:-2 width:1 gray8:11



[2.24 19:55:11]      [INFO]   [23] x:6 y:-2 width:1 gray8:66



[2.24 19:55:11]      [INFO]   [24] x:7 y:-2 width:1 gray8:221



[2.24 19:55:11]      [INFO]   [25] x:8 y:-2 width:3 gray8:255



[2.24 19:55:11]      [INFO]   [26] x:11 y:-2 width:1 gray8:173



[2.24 19:55:11]      [INFO]   [27] x:1 y:-1 width:1 gray8:19



[2.24 19:55:11]      [INFO]   [28] x:2 y:-1 width:1 gray8:70



[2.24 19:55:11]      [INFO]   [29] x:3 y:-1 width:1 gray8:171



[2.24 19:55:11]      [INFO]   [30] x:4 y:-1 width:1 gray8:234



[2.24 19:55:11]      [INFO]   [31] x:5 y:-1 width:1 gray8:247



[2.24 19:55:11]      [INFO]   [32] x:6 y:-1 width:1 gray8:215



[2.24 19:55:11]      [INFO]   [33] x:7 y:-1 width:1 gray8:197



[2.24 19:55:11]      [INFO]   [34] x:8 y:-1 width:3 gray8:255



[2.24 19:55:11]      [INFO]   [35] x:11 y:-1 width:1 gray8:225



[2.24 19:55:11]      [INFO]   [36] x:1 y:0 width:1 gray8:81



[2.24 19:55:11]      [INFO]   [37] x:2 y:0 width:1 gray8:250



[2.24 19:55:11]      [INFO]   [38] x:3 y:0 width:8 gray8:255



[2.24 19:55:11]      [INFO]   [39] x:11 y:0 width:1 gray8:246



[2.24 19:55:11]      [INFO]   [40] x:0 y:1 width:1 gray8:29



[2.24 19:55:11]      [INFO]   [41] x:1 y:1 width:1 gray8:243



[2.24 19:55:11]      [INFO]   [42] x:2 y:1 width:10 gray8:255



[2.24 19:55:11]      [INFO]   [43] x:12 y:1 width:0 gray8:248



[2.24 19:55:11]      [INFO]   [44] x:0 y:2 width:1 gray8:139



[2.24 19:55:11]      [INFO]   [45] x:1 y:2 width:11 gray8:255



[2.24 19:55:11]      [INFO]   [46] x:0 y:3 width:1 gray8:211



[2.24 19:55:11]      [INFO]   [47] x:1 y:3 width:3 gray8:255



[2.24 19:55:11]      [INFO]   [48] x:4 y:3 width:1 gray8:177



[2.24 19:55:11]      [INFO]   [49] x:5 y:3 width:1 gray8:22



[2.24 19:55:11]      [INFO]   [50] x:6 y:3 width:1 gray8:25



[2.24 19:55:11]      [INFO]   [51] x:7 y:3 width:1 gray8:175



[2.24 19:55:11]      [INFO]   [52] x:8 y:3 width:4 gray8:255



[2.24 19:55:11]      [INFO]   [53] x:0 y:4 width:1 gray8:245



[2.24 19:55:11]      [INFO]   [54] x:1 y:4 width:3 gray8:255



[2.24 19:55:11]      [INFO]   [55] x:4 y:4 width:1 gray8:34



[2.24 19:55:11]      [INFO]   [56] x:7 y:4 width:1 gray8:29



[2.24 19:55:11]      [INFO]   [57] x:8 y:4 width:4 gray8:255



[2.24 19:55:11]      [INFO]   [58] x:0 y:5 width:1 gray8:250



[2.24 19:55:11]      [INFO]   [59] x:1 y:5 width:3 gray8:255



[2.24 19:55:11]      [INFO]   [60] x:4 y:5 width:1 gray8:9



[2.24 19:55:11]      [INFO]   [61] x:8 y:5 width:4 gray8:255



[2.24 19:55:11]      [INFO]   [62] x:0 y:6 width:1 gray8:230



[2.24 19:55:11]      [INFO]   [63] x:1 y:6 width:3 gray8:255



[2.24 19:55:11]      [INFO]   [64] x:4 y:6 width:1 gray8:48



[2.24 19:55:11]      [INFO]   [65] x:7 y:6 width:1 gray8:9



[2.24 19:55:11]      [INFO]   [66] x:8 y:6 width:4 gray8:255



[2.24 19:55:11]      [INFO]   [67] x:0 y:7 width:1 gray8:181



[2.24 19:55:11]      [INFO]   [68] x:1 y:7 width:3 gray8:255



[2.24 19:55:11]      [INFO]   [69] x:4 y:7 width:1 gray8:191



[2.24 19:55:11]      [INFO]   [70] x:5 y:7 width:1 gray8:34



[2.24 19:55:11]      [INFO]   [71] x:6 y:7 width:1 gray8:17



[2.24 19:55:11]      [INFO]   [72] x:7 y:7 width:1 gray8:149



[2.24 19:55:11]      [INFO]   [73] x:8 y:7 width:4 gray8:255



[2.24 19:55:11]      [INFO]   [74] x:0 y:8 width:1 gray8:93



[2.24 19:55:11]      [INFO]   [75] x:1 y:8 width:11 gray8:255



[2.24 19:55:11]      [INFO]   [76] x:0 y:9 width:1 gray8:5



[2.24 19:55:11]      [INFO]   [77] x:1 y:9 width:1 gray8:207



[2.24 19:55:11]      [INFO]   [78] x:2 y:9 width:10 gray8:255



[2.24 19:55:11]      [INFO]   [79] x:1 y:10 width:1 gray8:32



[2.24 19:55:11]      [INFO]   [80] x:2 y:10 width:1 gray8:222



[2.24 19:55:11]      [INFO]   [81] x:3 y:10 width:9 gray8:255



[2.24 19:55:11]      [INFO]   [82] x:2 y:11 width:1 gray8:13



[2.24 19:55:11]      [INFO]   [83] x:3 y:11 width:1 gray8:126



[2.24 19:55:11]      [INFO]   [84] x:4 y:11 width:1 gray8:212



[2.24 19:55:11]      [INFO]   [85] x:5 y:11 width:1 gray8:246



[2.24 19:55:11]      [INFO]   [86] x:6 y:11 width:1 gray8:230



[2.24 19:55:11]      [INFO]   [87] x:7 y:11 width:1 gray8:159



[2.24 19:55:11]      [INFO]   [88] x:8 y:11 width:1 gray8:212



[2.24 19:55:11]      [INFO]   [89] x:9 y:11 width:2 gray8:255



[2.24 19:55:11]      [INFO]   [90] x:11 y:11 width:1 gray8:180



[2.24 19:55:11]      [INFO]   outline rect :(0.000000,-6.000000) - (12.000000,11.000000), w:13.000000, h:18.000000



[2.24 19:55:11]      [INFO]   overlapped rect :(0.000000,-6.000000) - (12.000000,11.000000), w:13.000000, h:18.000000由此可以得到:

  

填充区一共有94个span, 分布在坐标左上(0,-5) 右下(11,10) , 其矩形区域尺寸为(12,16)

沟边区一共有91个span, 分别在(0, -6) (12,11) 其矩形区域尺寸为(13,18)

填充与沟边的合成位图最终需要尺寸(13, 18)

妈呀,一个g 有94 + 91 = 185个span 只是一个g.

但是鉴于计算机处理的速度比我快,所以先忽略继续往下走起。

 

范例中,设置颜色到bitmap的代码是:

charBitmap[(int)((imgHeight - 1 - (s->y - rect.ymin)) * imgWidth + s->x - rect.xmin + w)] = outLineColor;

所以Bitmap 与 fillRect的坐标映射关系是:

y = (imgHeight - 1 - (s->y - rect.ymin)) * imgWidth

x = s->x - rect.xmin

 

实际上是完成形如下图的坐标变换:

LOG.ZS.0001.基于Freetype的游戏字体渲染优化思路

 

那么实际上,可以较为轻松的分别保存outlineBitmap 和 fillBitmap , 用于存储灰度.并记录offsetX, offsetY 作为坐标参考系对齐的依据。

但是,后来回过头来考虑这个问题,我干嘛要分别存储2个bitmap, 难道是像复用fillBitmap么? 没有啊,所以我不用分别存储outlineBitmap和fillBitmap,仅需要存储一个总的bitmap, 其大小是outline rect 和 fill rect 的bound rect.然后将两个灰度存入就行了。实际上,由于很多设备不支持2通道纹理,这里我选择采用RGBA8888的格式存储, R通道存储填充灰度, G通道存储边框灰度。浪费了2个通道实在属于无奈之举,但shader可以直接拿到这个texture做渲染,也是件好事。

 

除了bitmap需要关注,还需要取得相关的char metrics 并缓存下来, 因为Layout逻辑需要用到这些位置相关的数据。具体需要的值参考下图:

 LOG.ZS.0001.基于Freetype的游戏字体渲染优化思路

另需要注意的是,由于对边框的支持, 所以可能会对一些位置数据进行修正。

需要修正的数据包括:

bearingX, bearingY, width, height, advance.

如果不调整会这样?那么在字符有边框的时候,后面的字符很可能把前面的盖住。

怎么调整呢,嗯,嗯。直接写代码调整,对。

 

所以整理下载,缓存的结构体大概是这样的:

CharChache

bitmap : 纹理位图(r,g有用处, b,a 没用, 也许将来可以写点啥在里面)

charMetrics

       -bearingX

       -bearingY

       -advance

       -width

       -height

 

那么缓存的Key怎么考虑呢

Key包含一下部分:

char (uint16) 字符的unicode

font_size(uint8)

outline_size(unit8)

 

font_size 和 outline_size 采用uint8的原因是, 这样可以用一个32位整形数来作为缓存对象的键值,使用超过一个字节的情况存储font_size 和 outline_size 不做考虑,因为那种情况太极端了。

 

剩下的工作就相当的轻松了.我可以把大致需要的接口做个列表:

 

void renderString(string _str, TextStyle _defaultStyle, Texture2D _outTexture)

这个是整个文字渲染的进入点接口, 可以按Factory模式处理,目前实现FreeTypeWithBitmapCache 实现, 将来还可以搞个Device 实现,直接向操作系统要纹理,比如ios本身似乎是支持包边字体纹理生成的。

_defaultStyle 参数是文本的样式参数,包含行间距,字符间距,是否换行,换行宽度,首行indent,字体颜色, 边框颜色等信息.

总之,这个接口就是我把字符串给你,你按我要的效果给生成纹理。

 

void htmlParse(string _str, HtmlTagList _outHtmlTagList, string _outPureString)

该方法是解析富文本字符串(其实不是html,算是一种私有协议)。

输出的HtmlTagList 是解析的结果,他不是一颗树,实际只是一个链表而已(不想做那么复杂, 因为我压根就没打算支持标签的嵌套,简单实现即可,不必搞成浏览器,如果策划问为啥不支持嵌套表格,文本输入框, 嵌图片,为啥没有播放音频功能,直接耳屎扇飞)。

_outPureString 是纯文本的内容,实际也可以通过_outHtmlList 分析得到,但是这个方法输出来,作为顺便完成的工作.

 

ChatCacheEntry* queryCharCache(wchar _ch, uint8 _fontSize, _unit8 _outlineSize)

查询缓存, 如果缓存中不存在, 则按之前分析的逻辑生成ChatCacheEntry。

这里还有一个可优化的地方.就是可以在ChatCacheEntry中存放一个命中次数,内部按该次数对ChatChacheEntry进行排序,在适当的时候删除掉低命中次数的缓存条目,这样可以避免文字缓存把内存搞爆的情况。

 

void layout(string _str, TextStyle _style, vector<CharMetrics> _metrics, vector<Point> _outPos)

该方法接受字符串,样式,以及与字符串对应的每个字符的metrcis信息,该方法对整个字符串进行布局,并将位置输出到_outPos中。

始终 _str.length = _metrics.size = _outPos.size

 

所以最核心的方法就是上述这些。

其中客户端真正调用的就是 renderString, 其他接口都是为renderString服务,在renderString内部被调用的。

 

最后就是shader

shader很简单,

传入 fillColor, outlineColor

传入 纹理 texture

 

那么pixel颜色为

outlineColor.rgb = outlineColor.rgb

outlineColor.a = texture.r

outColor.rgb = outlineColor.rgb  + (fillColor.rgb - outlineColor.rgb) * texture.g

outColor.a = outlineColor.a + texture.g

 

然后,就基本上没有然后了。

云风有篇博文提到了一种方案,提出直接用一张大texture 作为文字的缓存。有兴趣可以去看下,不过我基于以下原因暂时不打算采用。

其一:这一版本我的目标仅是性能足够而非性能最优。

其二:大纹理并且随着新字符的生成和老字符的移除,同样会涉及主存和显存的换入换出,如果更新频繁,该方案是否总体上有益不能确定。

其三:我们的系统font_size 的尺寸不好估量12pt 和 24pt有可能都有,涉及到装箱算法,如果单页满了,还可能会做multi paging , 总的来说可能会增加大量复杂度。

东西一复杂了我就拉不出屎了。先看看吧,先看看。

 

所以目前还是先简单实现一版,看看性能如何再说。

该文档。。那啥,主要是整理自己的思路写下的日志和开发参考,大量细节都省略。所以估计很难阅读。也可能有超多的错漏。

 

嗯,嗯,明天开始编码。

现在先去看行尸走肉

 

你可能感兴趣的:(type)