⬆️ ⬇️

Exposing rasterization algorithms fonts (2/2)

(the second part of the translation of the article Exposing font rasterization algorithms )



Linux



Inheriting the worst



Windows rasterizes fonts badly, Linux is even worse. All Linux systems I've seen use FreeType [10] by David Turner, Robert Wilhelm and Werner Lemberg. This is an excellent library, but unfortunately it’s not a good way to use it. A typical screenshot of Linux looks like this:





')

Here is the full screenshot:

link



Immediately noticeable problem - black spots in rounded corners, formed as a result of smoothing. In general, it can be said that oblique strokes look heavier than vertical ones, which in the end gives the impression of “dirt”. You can argue that FreeType and Linux could use subtyx pixel rasterization similar to ClearType, but for me it does not give any noticeable advantages.







Look at “W”, “v” and “y” - the problem is essentially the same, the characters look dirty. You can slightly improve the situation in the corners, using gamma correction in the process of rasterization, but this still does not allow to achieve a perfect display.



Gamma Correction



Gamma correction works like this:







As you can see, rounded corners with anti-aliasing look much better with gamma 2.0. Gamma correction is a separate non-trivial topic, and, if you are interested, you can find comprehensive information in the Charles Point “Gamma FAQ” [6].



In our case, we are not talking about the curves "source signal - result" in electronic circuits, rather, the specifics of human vision. Visual response is roughly proportional to the square root of the physical luminosity. In other words, if we have two white pixels on a black background, and one of them emits two times more photons than the second, this does not mean that it will look twice as bright. In fact, about 1.4 times. You can easily verify this:







On the right - two pixels, and we can safely say that they emit twice as many photons per second than the pixel on the left. However, they do not look twice as bright. Four pixels will give about twice the brightness, but not two.



By omitting unnecessary explanations, we can say that there are two basic RGB color spaces: subjectively uniform, which is called sRGB, and physically uniform. In the latter, the value is proportional to the physical luminosity, as opposed to sRGB, in which the value is proportional to the subjective luminosity. Usually, a physically uniform color space is simply called “linear RGB”. When using anti-aliasing, color reduction should be performed in a linear space, but before output to the screen it is necessary to bring the colors to sRGB. In practice, this step is often ignored, and smoothing is performed directly in sRGB. In many cases, this gives an acceptable result, but not for rasterizing the text, which can be demonstrated directly in Microsoft Word. The thing is that they use some kind of trick to select text, something like a trivial inversion, instead of redrawing from scratch. With plain black-and-white anti-aliasing, selected text looks messy. With ClearType, it acquires a color border:







So we can conclude that Windows is correct gamma correction (but not for the selected text), in Linux it is usually ignored. FreeType can easily apply the desired gamma transformation to the black and white smoothing mask that the rasterizer generates. But it will work the same way as in Windows: inverting the colors will invert the gamma. In practice, this is useless, since gamma must be applied separately to each color component before mixing (which in practice is equivalent to working in linear RGB). Therefore, the gamma correction for a black and white mask will help only if you display black text on a white background. In this case, you can use the value in area 2 for correction. But if you display white text on a black background, you need to invert the gamma value, that is, use something around 0.5. The problem is that you do not know the exact color of the text and background, the text can also be displayed on top of a gradient or image. So the “black-and-white” gamma correction will not work, and the “full-color” gamma correction can be costly and difficult to implement. The problem is that for linear RGB you need more than 8 bits per channel, otherwise you will inevitably get a color loss. For text, this may be valid, but you cannot claim it for the entire desktop! And working in linear RGB, using 16 bits per channel, is still an impermissible luxury.



Gamma does not work



In fact, the situation is even worse. You can apply gamma correction with a value of 2 to the screenshot from Linux in the same Irfanview (Image-> Enhance colors ...) and look at the text. Please try not to pay attention to the fact that the icons look overexposed, concentrate on the text.







You like? I'm still not there. When I was working on rasterizing text in AGG , I thought that correct gamma correction could solve all the problems. Nothing like this! No matter how well it works, some elements look thicker, and some - thinner than vertical and horizontal strokes. This is very noticeable on sans serif fonts, and especially when the strokes are strictly pixel aligned. The problem is that TrueType hinting was specifically designed for a conventional black and white rasterizer without anti-aliasing! Using any anti-aliasing is formally incorrect, and most Linux systems do just that. The image below is the result of rasterization with anti-aliasing using both FreeType and GetGlyphOutline ().







The text looks lousy and it is very similar to what we see in most cases in Linux. No gamma correction will help here. For example, the best result I got with a gamma value of 1.5. Still looks bad:







Along the way, you should have noticed that, starting from a certain size, the text starts to seem heavy. This is exactly what is happening in Windows. If you turn off ClearType, it will be obvious (the text size is not saved exactly).







In general, you get the idea. To make it more obvious, we can enlarge the vector image that the GetGlyphOutline () function returns from the Win32 API, and see what happens.







This is how proprietary aggressive hinting works for a nominal size of 13 pixels. That is why the strokes in “k” look so thin, almost invisible. In italics, Times New Roman is still worse: the oblique stroke in “z” disappears completely. The distortions do not affect the normal rasterizer without smoothing, but the one that uses FreeType is sensitive to these things. He directly calculates the degree of pixel coverage, so that he honestly gets a zero coverage for the oblique z-dash. That is, it turns out that there is no point in interpreting TrueType bytecode for hinting (not to mention that you will have to buy a license). Smoothing is good, but it should not be applied for its own sake. In any case, I would prefer a non-anti-aliasing text if used in conjunction with inadequate hinting.



Autochinter FreeType



In FreeType version 2, David Turner introduced the auto-torque mechanism. It works quite well, but, nevertheless, its direct application gives a result that is far from ideal. Look at the result of rasterizing the Verdana headset with a gamut of 1.5:







Compare with the example without hinting:







The version without hinting definitely looks more accurate, but also more blurred. There are three main differences:



  1. Autohinting still produces errors in rounded small elements (the same visual difference in thickness between vertical and oblique strokes).
  2. Sometimes auto-quoting leads to incorrect kerning, as in “og” in the word “Dog” (in this example, a kerning table from a font was used).
  3. Auto-hinting leads to the same problem of accumulating an error throughout a line of text, with the result that the right edge of the text acquires “notches”.


Autochinter works better with more complex fonts, such as Times New Roman, but the same positioning problems still occur.



What to do?



Looking ahead, I'll show you another example.







Still, it is possible to find an acceptable solution! But first you need to agree that there is no way to use hinting of any kind and at the same time maintain the correct position of the text on any scale. Only text without hinting, with its natural blur. However, we can improve rasterization, although we will have to sacrifice something not very important. Namely, we can afford a small inaccuracy in the vertical positioning and height of the text. Among other things, TrueType hinting works in the same way: lines of text with a height of, say, 12 and 13 pixels will have the same height on the screen, although they will look different.







In short, for a nice looking text and at the same time maintaining accurate horizontal positioning, we need the following:



  1. Use horizontal sub-pixel RGB anti-aliasing on LCD monitors.
  2. Use only vertical hinting and completely abandon the horizontal.
  3. Use exact glyph offset values ​​calculated at high resolution for non-hinting glyphs.
  4. Use exact high resolution values ​​from the kerning table.


A small gamma correction may improve the result, but this is not necessary. The text looks good even directly in sRGB, which means that there will be no problems with inverted color schemes.



You can easily achieve acceptable results with FreeType and its autochinter. This means that you don’t need to worry about licensing TrueType proprietary hinting. The same can be done using the GetGlyphOutline () function from the Win32 API. It is more difficult, but still possible.



Subpixel rasterization in RGB



You can find a comprehensive guide on the use of sub-pixel rasterization in RGB on the pages of Steve Gibson, "Sub-pixel rasterization technology" [2]. I also tried to use this technology with 64-level raster images with anti-aliasing that GetGlyphOutline () can generate: Maxim Shimanarev, "How ClearType works in Windows Longhorn" [3] (UPD: link is rotten, but you can read here , here or here ). You can download the Windows demo program with all the sources at this link:

antigrain.com/stuff/lcd_font.zip



In addition, I wrote a simple, “on the knee for the evening,” rasterizer for AGG . It can be found in the demo examples that follow. At the moment, the code is unsafe and rather slow. This is normal for demonstration programs, but is unacceptable in a real project, primarily due to the fact that it uses a temporary buffer for no more than 2,048 pixels in the stack.



In the simplest case, all we need is the transparency values ​​for each color channel. In this file, agg_pixfmt_rgb24_lcd.h. I also used the extra blur that Steve Gibson describes. It runs on the go, although it can be done in advance using some kind of caching mechanism. In this case, it will work much faster, at least not slower than classical alpha blending.



For debugging channel blending, I used Brian Fraizen’s ZoomIn [9] program. I added “decoding” triples of colors on all scales that are multiples of three. You can download the executable file here: antigrain.com/stuff/ZoomInLcd.zip (in 2005, I lost the modified source code. In any case, these modifications are easy to do on your own). You can compare the enhanced results of conventional black and white and subpixel RGB rasterization:





Black and white rasterization





Subpixel RGB rasterization



Other nuances



To keep the vertical hinting, but to get rid of the horizontal, we will simply deceive the hinter: we stretch the characters horizontally so that the hinter is forced to work with high precision. The problem is that the AGG font engine for FreeType uses inaccurate offset values, given hinting. Technically, a hinter should calculate the exact offset values ​​for strongly stretched glyphs, but for some reason it does not. I had to modify it in order to use the original, “non-screwed” offsets. A modified version is also included in the demo programs. Once the glyph curve is obtained, we use an affine transformation to compress it back. In the simplest case, that's all we need. The kerning table contains fairly accurate values.



So I want to turn to David Turner: maybe it makes sense to add to his autochinter an option that would allow performing hinting only on the Y axis, ignoring the hinting on the X axis? Or you can make a separate 1-D hinter, much simpler than the existing one. As you will see, the text with sub-pixel RGB rasterization looks very similar to the Adobe Acrobat Reader, and, in any case, is much better than in any modern Linux system (the article was written in 2007 - approx. Transl. ). I believe this will help promote and increase the popularity of Linux based systems.



Using the Windows API is much more complicated. The GetGlyphOutline () function returns the offset value in integer pixels, which is too rough for us. Stretching does not save. There are more functions like GetCharABCWidthsFloat (), but they are useless, since they calculate values ​​for hint glyphs and despite the fact that they contain floating-point numbers, it’s still, in fact, integers. So I did not find an easy way to get accurate offsets. As a result, I had to use two fonts at the same time, one 1024 pixels high, and the other the size we need, with hinting and a stretched affine matrix. I admit that I could miss something, but I have no thoughts about how this can be implemented more correctly. Perhaps, in Microsoft Word, they use some undocumented functions, which is absolutely unfair from the point of view of competition. Of course, I cannot be completely sure of this, but the situation makes me think that Microsoft deliberately does not provide a sufficiently good API for developing WYSIWYG document management tools. This is a typical policy of a monopolist, which as a result leads to inhibition of technical progress.



In fact, everything is worse. Proprietary hinter does not work with a “stretched” matrix! At a minimum, I did not find any scaling factor that would correctly process the glyphs. Only the 1: 1 scale worked correctly, but as a result I got the same problems that forced me to use a black and white rasterizer without smoothing:







It looks terrible, is not it? Any scaling led to badly damaged glyphs. Here, for example, italics Times New Roman (16x stretching horizontally):







Or even so. Arial (stretching 100 times horizontally) - funny smudges, right? But it is impossible to read:







I think it makes no sense to say that the FreeType autochinter works correctly with any stretching.



It looks as if the Microsoft API is just a collection of unhealthy, random solutions “on the knee” without any engineering culture and global idea behind all this. As a rule, you can use Microsoft software only in one strictly predetermined way. Step to the right, step to the left - and all is lost. It may be good for their business, but at least unfair. Such a policy violates the conditions of equal competition and as a result slows down the overall progress in the market. The antimonopoly committee should pay attention to this situation, instead of ridiculous demands to remove Media Player or Internet Explorer from the system.



In the end, I found out that the value "16" is the lesser evil, it is suitable for most cases, but still does not work for the Times New Roman italics.



Demonstration program



Here it is, a Windows program using TrueType:

www.antigrain.com/research/font_rasterization/truetype_test_02_ft.zip



And here is the version that uses the Windows API:

www.antigrain.com/research/font_rasterization/truetype_test_02_win.zip



The version under FreeType requires the following files in the program directory: arial.ttf, ariali.ttf, georgia.ttf, georgiai.ttf, tahoma.ttf, times.ttf, timesi.ttf, verdana.ttf, verdanai.ttf. You can find them in the% WINDIR% \ Fonts folder.



If you want to compile it, download AGG version 2.4 or 2.5 and unpack the files somewhere like agg-2.4 \ research \ win32 \ trutype_lcd \ *. *. For the FreeType version, you will also need to build FreeType yourself, and possibly change the project settings.



A program can also be compiled for Linux / X11 or another system if you write an appropriate makefile, similar to those used in the AGG examples.



Text in FreeType and WinAPI versions looks different due to different hinting algorithms.







You see here a large number of settings. First, we can change the fonts, and also enable or disable kerning, hinting and subpixel RGB rasterization. In addition, you can invert the image to get white text on a black background.



Slider "Font Scale" allows you to smoothly change the font size. As you can see, when hinting is on, lines are attached to pixels, but the width of the text continues to change smoothly. You can better see this effect by changing the interval. Without hinting, the layout of the text is perfectly preserved on any scale, but the text looks blurry. Vertical pegging of lines is the most reasonable compromise between sharpness and accuracy of text layout. I myself am shocked by how much vertical hinting improves quality while preserving the shape of characters.



The interval, width, and “imitation italics” sliders, I think, do not need comments. For people familiar with computer graphics, it is obvious that these are ordinary affine transformations . I would like to note only one fact: in the “black-and-white” and “sub-pixel RGB rasterization” modes the “italic imitation” slider works a little differently. This is because I was too lazy to process its values ​​correctly, through arctangents. In any case, it is irrelevant.



The function I'm particularly proud of is “imitation of bold.” It works like this:







There is one more simple trick. AGG has a utility called conv_contour, which allows you to calculate an equidistant for a given polygon. But using it directly gives a too vague result, and also significantly changes the shape of the characters (although this can be useful for glow and shadow effects):







Blur is easy to avoid. We stretch the glyphs vertically, say, 100 or 1000 times, calculate an equidistant polygon and compress it back. So as a result, the coordinates along the Y axis almost do not change and the text remains clear. The demonstration program has a class of "faux_weight". Again, it's amazing how many features free horizontal scaling gives. And no less amazing, as far as binding to the pixels vertically improves the visual result.



Another example (I love this freedom):







This is still the same Georgia headset, but only programmatically converted. It is quite readable, clear and smooth at the same time (yes, I agree, hand kerning would not hurt her).



Or the same for the Tahoma headset:







The gamma adjustment slider controls the gamma correction (it is performed separately for each channel). Theoretically, one should apply an “immediate gamut” to the original colors, and then, after drawing the scene, apply an “inverted gamut”. But, since the text in these examples is always white or black, there is no point in the first operation.



The primary weight slider controls the distribution of energy as described by Steve Gibson: www.grc.com/freeandclear.htm . Enough to manage only the primary weight, and the rest to count accordingly. By increasing the primary weight, you can make the text clearer, but a colored outline appears around the signs. It makes sense to use values ​​up to 0.5, for large values ​​the color “glow” becomes too noticeable. As for me, Windows ClearType also gives too noticeable color "glow".



Such rasterization can work quickly.



The demo program is pretty slow. Partly because vector operations are performed on the fly, but primarily because of the WinAPI function GetGlyphOutline (), which itself is terribly slow. On the other hand, such rasterization can be no less fast than any hardware accelerated rasterization of text. But for starters, you have to agree that accelerating the rasterization of arbitrarily changing text while preserving hinting, correct markup, and precise sub-pixel quality is not an easy task in principle. By arbitrary transformations, I mean truly arbitrary ones, including perspective and any non-linear transformations.



Most of the time, we have to deal with horizontal text, even when using East Asian languages. In addition, most of the time glyphs use the same nominal size. From this it follows that a caching mechanism would be useful here. A sub-pixel black and white mask for three RGB channels requires three times more memory, but at the same time gives text positioning accuracy up to 1/3 of a pixel. In most cases, this works quite well. Unless for theoretical “luxurious” rasterization, you can use two black-and-white masks on the glyph, getting 1/6 pixel accuracy. Even software alpha blending works fairly quickly - about 2-4 ms per glyph on modern Intel or PPC processors. Using a GPU this can happen even faster if you load the appropriate textures. The only problem is that the GPU should allow alpha blending across channels, which, as far as I know, seems possible. At a minimum, David Brown mentions this in his presentation [8]. But I could not find more information on this topic (how to get a 6-channel output from a pixel shader) and I would be grateful if you would offer me any links on this topic.



Links



  1. Joel Spolsky, Font smoothing, anti-aliasing, and sub-pixel rendering.

    www.joelonsoftware.com/items/2007/06/12.html
  2. Steve Gibson, Sub-Pixel Font Rendering Technology.

    www.grc.com/cleartype.htm
  3. Maxim Shemanarev, Inside ClearType in Windows Longhorn.

    www.byte.com/documents/s=9553/byt1113241694002/0411_shemanarev.html

    (requires online registration.)
  4. FontFocus white paper, artofcode.com/fontfocus
  5. Jeff Atwood, Font Rendering: Respecting the Pixel Grid.

    www.codinghorror.com/blog/archives/000885.html
  6. Charles Poynton, Frequently-Asked Questions about Gamma.

    www.poynton.com/GammaFAQ.html
  7. Dave Shea, A Subpixel Safari.

    mezzoblue.com/archives/2007/06/12/a_subpixel_s
  8. David Brown, Avalon Text. A PowerPoint presentation.

    download.microsoft.com/download/1/8/f/18f8cee2-0b64-41f2-893d-a6f2295b40c8/TW04007_WINHEC2004.ppt
  9. Brian Friesen, ZoomIn Program.

    www.csc.calpoly.edu/~bfriesen/software/zoomin.shtml
  10. David Turner and the others, FreeType font Library.

    freetype.org
  11. Jim Mathies, XP Style DPI Scaling.

    www.mathies.com/weblog/?p=908
  12. Long Zheng, Windows Vista DPI scaling: my Vista is bigger than your Vista

    www.istartedsomething.com/20061211/vista-dpi-scaling


PS: The first part of the article in the web archive (with pictures). The second part of the article in the web archive (with pictures).



PPS: Some interesting links for people interested in screen typography:

Font size: not everything is so simple

Fonts on a budget monitor in Windows 8

High DPI values ​​in Windows OS

Protection of programmers

Source: https://habr.com/ru/post/112400/



All Articles