Subpixel rendering is technology for creating smooth text readable at low sizes on LCD displays. In Windows XP, this is known as Cleartype, but the more general name is “subpixel rendering”. How it works in detailed can be read at the previous link, or at grc.com (a good resource for this topic)
But the problem I was facing today was that even though FreeType gives you a lot for free (using FT_Render_Glyph and specifying FT_RENDER_MODE_LCD), the results are not as pretty as the results from ClearType due to extreme colour fringing. They are actually really ugly, and the solution is to perform a low-pass filter stage after rendering. The FreeType crew is very well aware of this, but thinks that the filtering should be done by the application, but how?
The problem has been explained and solved earlier, but there are few direct approaches. The idea is to look at the neighbour sub-pixels (not pixels as I first thought) and let them impact the value of the subpixel you’re currently looking at. If we want to filter the green part of a pixel in a RGB-screen (look at the images at the previous articles), then we must add some of the colour value of the left subpixel (which is red) and the right subpixel (blue). For even better results, we can expand this to more pixels to the left and right.
Ready for the solution? If we assume that you’ve rendered all your glyphs to a buffer, you can use this (python inspired) pseudo-code to perform a low-pass 5-tap FIR-filter:
; source is the buffer where the glyps are rendered using FT_RENDER_MODE_LCD (not the actual slot->bitmap one though!)
; dest is a buffer as big as the source buffer.
c = (1, 2, 3, 2, 1) ; Use any number you want, but a centered distribution will give better results
c = c / sum(c) ; (1/9, 2/9, 3/9, 2/9, 1/9)
for y in rows(source):
for x in columns(source[y]):
dest[x, y].Red = c[0]*source[x-1, y].Green + c[1]*source[x-1,y].Blue + c[2]*source[x,y].Red + c[3]*source[x,y].Green + c[4]*source[x,y].Blue
dest[x,y].Green = c[0]*source[x-1, y].Blue + c[1]*source[x,y].Red + c[2]*source[x,y].Green + c[3]*source[x,y].Blue + c[4]*source[x+1,y].Red
dest[x,y].Blue = c[0]*source[x, y].Red + c[1]*source[x,y].Green + c[2]*source[x,y].Blue + c[3]*source[x+1,y].Red + c[4]*source[x+1,y].Green
This assumes that the screen has its pixel in a RGB-order. For the edge pixels, where x-1 or x+1 isn’t available, the center pixel (and colour) can be used instead.
You might want to try to play with the coefficients, c, to try other weights.