Text Rendering in the QML Scene Graph
http://labs.qt.nokia.com/2011/07/15/text-rendering-in-the-qml-scene-graph/
Some time ago, Gunnar presented to you what the new QML Scene Graph is all about. As mentioned in that article, one of the new features is a new technique for text rendering based on distance field alpha testing. This technique allows us to leverage all the power of OpenGL and have text like we never had before in Qt: scalable, sub-pixel positioned and sub-pixel antialiased… and at almost no cost.
First, here is a video showing the improvements this technique brings:
用distance field 来渲染text,能充分发挥opengl的能力:缩放,sub-pixel positioned,sub-pixel消锯齿。。。
http://www.alienbill.com/2600/cookbook/subpixel.html 两个字节来表示位置
http://www.grc.com/ctwhat.htm Sub-Pixel Rendering
Distance Field… Huh?
Now, most of you are probably wondering what a distance field is. Also known as distance transform or distance map, it is a derived representation of a digital image that maps each pixel to a value representing the distance to the closest edge of the glyph. On a range from 0.0 to 1.0, a pixel on the edge of the glyph should have a value of 0.5, a pixel outside the shape should have a value going towards 0.0 as the distance from the nearest edge increases, a pixel inside the shape should have a value going towards 1.0 as the distance from the nearest edge increases.
边缘上时0.5,边缘外面的越往外就越接近0.0,边缘内越往内越接近1.0
A distance field can be generated either from a high-resolution image or from a vector-based representation of the glyph. The first solution is typically based on a brute-force method and can potentially be very slow, thus it is hardly conceivable to use this solution in our case when we have dozens of glyphs (or even hundreds in the case of Chinese) to generate at once. Some tools (like this one) allow to pre-render a set of glyphs from a given font into a file to be then used at run-time, but we didn’t choose this solution as it gives poor flexibility to developers.
distance field可以由高分辨率的图片或者矢量形式的字符产生。 第一种方案太慢且不灵活,所以我们不采用。
We chose instead to use a vector-based representation of the glyph to generate the distance data. Put simply, we extrude the outline of the glyph by a fixed distance on the inside and on the outside and fill the area between the extruded outlines with a distance gradient. We store the result in a single 8-bit channel of a 64×64 pixels cell contained in a bigger texture. By doing so we manage, thanks to Kim, to generate a distance field in less than a millisecond per glyph on a mobile device (on average), making any vector font usable dynamically at run-time.
我们采用矢量形式的字符来产生distance data。简单的说,我们把字符往内部和外部延伸固定的距离,并用距离梯度填充。
How Does It Work
This technique allows to take advantage of the native bilinear interpolation performed by the GPU on the texture. The distance from the edge can be accurately interpolated, allowing to reconstruct the glyph at any scale factor. All we need to do is alpha-testing: pixels are shown or discarded depending on a threshold, typically 0.5 as it is the value at the edge of the glyph. The result is a glyph with sharp outlines at any level of zoom, as if they were vector graphics. The only flaw is that it cuts off sharp corners, but this is negligible considering how bad a magnified glyph looks when this technique is not used.
这个技术使得我们可以充分利用GPU的固有的双线性插值的能力。边缘的距离可以被精确的插值,这样字符就能被无限缩放。我们所要做的就是alpha测试:像素保留或丢弃取决于阈值,一般是0.5,因为这个值时边缘的值。这样字符就能在任何放大的倍数的情况下有清晰的边缘,好像它们是矢量图形一样。唯一的缺点是尖角被去除了,但这个可以忽略不计。
This technique provides a great visual improvement while not affecting performance at runtime as everything is done “for free” by the graphics hardware.
Improvements
High-Quality Anti-Aliasing
Using the same distance field representation of the glyph, we can also do high-quality anti-aliasing using a single line of shader code:
varying highp vec2 sampleCoord; uniform sampler2D texture; uniform lowp vec4 color; uniform highp float distMin; uniform highp float distMax; void main() { gl_FragColor = color * smoothstep(distMin, distMax, texture2D(texture, sampleCoord).a); }
Instead of using a single threshold to do alpha-testing, we now use two distance thresholds that the shader uses to soften the edges. The input distance field value is interpolated between the two thresholds with the smoothstep function to remove aliasing artifacts. The width of the soft region can be adjusted by changing the distance between the two thresholds. The more the glyph is minified, the wider the soft region is. The more the glyph is magnified, the thinner the soft region is.
When the GPU is powerful enough (meaning desktop GPUs) we can even do sub-pixel anti-aliasing, it is just about adding some lines of shader code. Instead of using the distance data to compute the output pixel’s alpha, we use the data of the neighboring pixels to compute each color component of the output pixel separately. Five texture samples are then needed instead of one. The red component averages the three left-most distance field values, the green component averages the three middle distance field values and the blue component averages the three right-most distance field values. Because this requires more processing power, sub-pixel anti-aliasing is currently disabled on mobile platforms. Anyway, the high pixel density displays that equips mobile devices nowadays make the use of sub-pixel anti-aliasing pointless.
sub-pixel anti-aliasing用相邻的点来计算alpha,而不是用距离来计算。这样就需要5个样本值而不是一个。 红色的部分用最左的三个distance field值来计算(去均值),绿色的的由三个中间distance field值来算,蓝色的有最右的来算。
Special Effects
In addition to anti-aliasing, the distance field can be used, again with just a few lines of shader code, to do some special effects like outlining, glows or drop shadows. We are then able to implement the three styles provided by the QML Text element (outline, raised and sunken) using that technique. For example to do outlining, we just have to use a different color for the texels which are between two distance values. Effectively, it makes the use of these effects faster, nicer and scalable (scaling a styled text in QtQuick 1.0, not Scene Graph based, gives very poor quality).
特效:
distance field 除了可以消锯齿,还可以用来做描绘 轮廓outlining, glows or drop shadows。
Implementation Details
For a given font, we rasterize and cache each glyph (or actually its distance field representation) only once at a size of 64×64 pixels. The same texture is then used to render the glyph at any size by scaling it appropriately. In comparison, the native font rendering used by QPainter implies caching a separate version of the glyph for each size used in the application. This leads to a lower graphics memory consumption when using different sizes of the same font.
To be able to scale freely the glyphs, we need to disable font hinting to have correct glyph positions at any level of zoom. As a consequence glyphs are also sub-pixel positioned (i.e. not pixel aligned).
实现细节:
特定的字体,我们光栅化(点阵化,栅格化,矢量转化为位图,The process of converting a vector image into a bitmap image),并cache每个字符为64x64像素大小一次。
同样的texture被用来渲染任何大小的字符。 作为对比,原理的QPaintercacheing每个大小的字符。这就节约了内存。
为了能够随意缩放字符,我们需要关闭font hinting(字体微调) ,这样字符才能在任意缩放的情况下有正确的位置。因此,字符也是sub-pixel positioned 的。
http://www.freetype.org/freetype2/docs/glyphs/glyphs-5.html 这边介绍了freetype2的Sub-pixel positioning相关信息。大致是用分数(fractional )来表示位置。
Hack It Your Way
It you haven’t already, grab the Qt 5 repositories and look for the files starting with qsgdistancefield in the QtDeclarative module.
- For debugging purposes you can disable distance field text rendering with the QML_DISABLE_DISTANCEFIELD environment variable.
- On desktop platforms sub-pixel antialiasing is enabled by default, pass the –text-gray-antialiasing option to qmlscene to use the standard gray antialiasing.
For more reading on the topic have a look at this paper from Valve.
------------------------------------------------------------
Font hinting (also known as instructing) is the use of mathematical instructions to adjust the display of an outline font so that it lines up with a rasterized grid. At low screen resolutions, hinting is critical for producing a clear, legible text. It can be accompanied by antialiasing and (on liquid crystal displays) subpixel rendering for further clarity.