There were a number of problems with the old way of drawing text. The first is that fonts were stored on the server (remember, the server in X terminology means the part that drives the screen) and exposed via a font server. This often didn’t provide enough information for things like accurate kerning to client processes, so they needed to access the font information separately. It also meant that there was a lot of client-server communication required for things like enumerating fonts to populate font list boxes, increasing latency on remote connections.
Remote X had some serious issues for font rendering. If you install an application on machine A and then connect to it from machine B, you will not be able to see the fonts that were installed along with the application. This is even worse when trying to render things like PDFs, where the font information is in the document itself and can’t be stored in the server.
To get around this, a lot of toolkits began rendering text on the client side and drawing them on the screen as images. This was somewhat better, since it meant that text display was better and made it easier to add fonts, but it suffered from a few disadvantages, such as increased overhead and the fact that each toolkit used its own mechanism for displaying fonts—meaning that fonts were no longer shared between all applications.
XRender fixes this. It removes the font server and delegates its functionality to a shared library run on the client machine. This enumerates fonts and produces images of the glyphs.
When glyphs need to be rendered, they are sent to the server, where they are cached and composited using the Render extension, hopefully using hardware acceleration (somewhat ironically, drawing text is one of the most computationally expensive parts of a modern GUI).
Traditionally, X font glyphs were stored as one-bit images. Each pixel was either on or off. This made it hard to provide antialiasing. Modern fonts are typically stored as a series of bezier curves. When a pixel is covered by one of these curves, it is set (e.g., black). When it is not covered, it is clear (e.g., white). Unfortunately, a lot of the time there are pixels that are partially covered. With old X fonts, this was done using a simple thresholding mechanism.
If a pixel were more than 50% covered by a curve, it would be set; otherwise it would be clear. This approach makes it simple to generate the glyphs and simple to draw them, but tends to look ugly. Antialiased text uses a grey value to represent the edges. For 8-bit glyphs, if a curve covers 25% of a pixel, the pixel value will be set to 64.
This can be taken a step further on LCD screens. These have a regular arrangement of square pixels with red, green, and blue components. These can be treated as individual pixels to give more horizontal resolution. If your display has pixels in the RGB order, then a single white pixel can be represented 1/3 of a pixel over by having a cyan pixel to the left of a red one; you have the adjacent green, blue and red sub-pixels turned on.
This can be done with the render extension by generating glyphs with a greater horizontal resolution and compositing them using sub-pixel information retrieved from the RandR extension.
RandR is also worth a mention. While not impressive in the eye-candy sense, its addition makes X a lot more pleasant to use. It provides a mechanism for modifying the resolution and orientation of the screen at runtime, for example when a new monitor is plugged in. It also allows extra information, such as the pixel order, to be queried from a display.