任何有多年客户端开发经验的开发者都应该知道复杂的文字渲染是怎么工作的。至少在2010年以前,我刚开始写libhwui的时候(这是一个基于Android2.0的2D绘画库),我就意识到处理文字有时会比其他方面更复杂,特别是当你尝试用GPU在屏幕上进行绘制的时候。文字与AndroidAndroid上的文字渲染加速器硬件最初是由Renderscript团队写的,然后被很多工程师改进和优化,包括我和好友Chet Haase。在网络上,可以很容易找到很多关于怎么使用OpenGL ES渲染文字的教程。如果觉得还不够,可以看看关于游戏的文章,只看关于文字渲染部分就行。 本文说不是很新奇的知识,只是对于很多开发者来说,通过本文可以从深层次上了解如何实现一个基于GPU的文字渲染系统,文章最后还介绍了一些比较容易实现的优化方法。 用OpenGL渲染文字的常用方法是计算包含所需字形的所有纹理集。这个操作通常是使用一些相当复杂的算法进行离线操作,这样可以在构造字形的时候更加高效。在创建这样一个纹理集之前,首先需要知道应用程序在运行时要使用的字体,包括字体样式、大小以及其它属性。 在Android上,提前进行字体纹理生成不是一个实用的方案。Android上的UI工具并不能知道应用系统会使用什么字体和字形,并且应用还可以在运行时载入自定义的字体,这是主要的限制。Android字体渲染还必须遵循以下条例:
字体渲染器的实现在进入底层OpenGL字体渲染器工作原理之前,我们先从应用层使用的高级别的API开始。这些API对于理解libhwui很重要。 文字API用于布局和绘制文字主要有4个API:
TextView和android.text的都是在Paint和Canvas上的高级API。Android3.0以后,Paint和Canvas直接被实现在Skia之上,这是一个开源的渲染库。SKia提供了一个很好的Freetype抽象实现,这是一个很热门的开源字体栅格化程序。 对于Android4.4,情况变得有些复杂。Paint和Canvas都使用了一个内部的JNI API,叫做TextLayoutCache。它可以处理复杂的文字布局(CTL)。这个API依赖Harfbuzz,一个空间开源的字形引擎。TextLayoutCache的输入是一个字体和一个Java的UTF-16的字符串,输出是一个带有x/y坐标的字形列表。 TextLayoutCache是支持非拉丁语言的要点,比如阿拉伯语言、希伯来语、泰国语等,本文不会解释TextLayoutCache和Harfbuzz的工作原理,但本人强烈建议读者去学习学习CTL。如果在开发应用的时候需要支持非拉丁语言环境,那么就要学习它了。如果你曾经参与过OpenGL渲染文字的文章中的讨论,就会发现这种特殊的问题是很少见的。绘制文字比简单排布字形更复杂。某些语言中,比如阿拉伯语是从右到左的,还有泰语甚至需要把字形排布在前一个字形的上面或者下面。 也就是说,当直接或间接调用Canvas.drawText()函数的时候,OpenGL 渲染器不会收到你发送的参数,而是收到一串数字、符号标识,还有x/y 坐标集合。 |