Using XRender with X11 to render antialiased fonts

The last week have been spent trying to figure out how to make XRender work with X11. It took a week because I have skill issues and there is pretty much no example out there on how to do this. There are plenty of examples of printing strings but to render font within a buffer there weren't any, at that I found. The reason I wanted to print fonts using buffer was for sake of simplicity.

What is antialiasing a font?

Fonts that are not antialiased have jagged edges or appear pixelated which makes them hard to read for smaller font sizes. I got to see this first hand as I have been working with jagged edges the last week. As far as I understand font is antialised by blurring the edges which makes them appear smoother and increases readability by a lot. The difference is phenomenal.

The font themselves which are antialiased are just alpha values per location. This took me a while to understand. As it turns out X11 only renders RGB values. So in a 32 bit number the first 8 bits don't do anything. This is where XRender comes in.

Some things to know

I've been writing a buffer to a X11 window by creating an image and putting this image on the window. Now to work with XRender you need to write to XRender structure called Pixmap and then manipulate the Pixmap. This happens on the server side X11. Once you write your buffer to this Pixmap you then have to Composite or sort of blend this Pixmap with a destination Pixmap which effectively writes it to the Window. There are various ways you can do this blending referred to as Compositing. The thing I struggled with most was this understanding and then a while longer to realize that X11 can put an image on a drawable. This need not be just the window, you can make the Pixmap the drawable. Which is what XRender provides to manipulate the buffer. Finally you can now set the format to include Alpha values. I'll be sharing code for all of this, so it should make a lot of sense.

// render here writes the buffer to the X11 window via the pixmap

fn render(screen_buffer: *common.OffScreenBuffer, display: ?*c.Display, window: c.Window) void {
    var wa: c.XWindowAttributes = undefined;
    _ = c.XGetWindowAttributes(display, window, &wa);
    // default is 24, it's changed to 32 to include alpha values
    const depth = 32;

    // Note: xrender operations are server side (x11)
    // Operations on the image are done once they are sent to the x11 server
    //
    // Steps as I understand them so far:
    // Write the bitmap onto a pixmap (client side) using putimage
    // Create a picture from this pixmap
    // Create a second picture on the window
    // Composite the two pictures above
    const pixmap = c.XCreatePixmap(display, window, screen_buffer.window_width, screen_buffer.window_height, @intCast(depth));

    const image = c.XCreateImage(
        display,
        null,
        @intCast(depth),
        c.ZPixmap,
        0,
        @ptrCast(&screen_buffer.memory),
        @intCast(screen_buffer.window_width),
        @intCast(screen_buffer.window_height),
        BitmapPad,
        @intCast(@sizeOf(u32) * screen_buffer.window_width),
    );

    const pixmap_gc = c.XCreateGC(display, pixmap, 0, null);
    _ = c.XPutImage(display, pixmap, pixmap_gc, image, 0, 0, 0, 0, @intCast(screen_buffer.window_width), @intCast(screen_buffer.window_height));

    const src_format = c.XRenderFindStandardFormat(display, c.PictStandardARGB32);
    const src = c.XRenderCreatePicture(display, pixmap, src_format, 0, null);

    const dst_format = c.XRenderFindVisualFormat(display, wa.visual);
    const dst = c.XRenderCreatePicture(display, window, dst_format, 0, null);

    c.XRenderComposite(
        display,
        c.PictOpOver,
        src,
        0,
        dst,
        0,
        0,
        0,
        0,
        0,
        0,
        @intCast(screen_buffer.window_width),
        @intCast(screen_buffer.window_height),
    );
}

You can find the entire file here.

You can find docs to XRender here. This guide on how composition works was quite useful to me to understand compositing. You can find methods within XRender here. As the first doc only has conceptual explanations. Which were quite useful too and it took a few reading to make sense.