Cairo

Cairo is a 2-D graphics library with support for multiple output devices. EGL is one of the backends for Cairo graphics, and this means that it can draw to Wayland surfaces. This chapter looks at this backend binding.

Resources

Introduction

Cairo is a 2-D graphics library originally developed for use in the X Window system, but abstracted away from that in early 2000's. With Cairo, most of the original X Window calls are redundant, and this is one of the drivers for replacing X with Wayland.

Cairo can be output to X, OS X Quartz, PNG files, PDF files, ..., and OpenGL. This last allows Cairo to be used to write to EGL surfaces, and this forms the basis of using Cairo with Wayland.

Cairo basics

Cairo uses as backend a device of type cairo_device_t. On a device it will create a surface of type cairo_surface_t to write to. Writing is done using a context of type cairo_t which holdsthe current state of the rendering device, such as the location of objects to be drawn, fonts, colours, etc.

The value of fields in the context can be changed by calls such as cairo_move_to and objects drawn to the surface by calls such as cairo_show_text.

The surface may then be rendered to the device by calls such as cairo_paint or written to a file by calls such as cairo_surface_write_to_png. The following program from the Cairo FAQ doesn't create a device, but instead creates a surface directly as an image surface, draws a string in blue to it and then writes the surface out to a PNG file:

	
#include <cairo.h>

int
main (int argc, char *argv[])
{
        cairo_surface_t *surface =
            cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 240, 80);
        cairo_t *cr =
            cairo_create (surface);

        cairo_select_font_face (cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
        cairo_set_font_size (cr, 32.0);
        cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
        cairo_move_to (cr, 10.0, 50.0);
        cairo_show_text (cr, "Hello, world");

        cairo_destroy (cr);
        cairo_surface_write_to_png (surface, "hello.png");
        cairo_surface_destroy (surface);
        return 0;
}
	
      

Creating a Cairo device for an EGL display

The functions to deal with EGL are not documented in the online Cairo documentation. Instead you have to look inside the cairo-gl.h header file. This defines the call cairo_egl_device_create() which takes an EGLDisplay and an EGLContext as parameters. To use this call in Wayland means that you to have initialised these to Wayland as described in the EGL chapter.

This can be used as in

	
init_cairo() {
    cairo_device =
      cairo_egl_device_create(egl_display, egl_context);
    if (cairo_device_status(cairo_device) != CAIRO_STATUS_SUCCESS) {
      fprintf(stderr, "failed to get cairo EGL device\n");
      exit(1);
    }
}	  
	
      

Creating a Cairo surface for an EGL surface

Once the Cairo device has been created, it can be used with an EGL surface to create a Cairo surface to render to:

	
cairo_surface = cairo_gl_surface_create_for_egl (cairo_device,
 						 egl_surface,
                                                 width, height);
	
      

Drawing to the Cairo surface

Once you have a Cairo surface, you draw to it using standard Cairo calls. These are not dealt with here - they would take a whole book. Instead, refer to the tutorials and documentation listed on the Cairo site.

Rendering the Cairo surface to Wayland

In the EGL chapter, an EGL buffer was sent to the Wayland compositor by EGLSwapBuffers. In Cairo, this wrapped in the call cairo_gl_surface_swapbuffers((). That's it! No other changes need to be made to standard Cairo programs.

Complete example

The following example cairo_window.c creates an EGL Wayland window, then a Cairo surface and draws "hello" in green on a yellow rectangle:


      

Other relevant calls

These are defined in cairo-gl.h:

	
cairo_public cairo_surface_t *
cairo_gl_surface_create (cairo_device_t *device,
                         cairo_content_t content,
                         int width, int height);

cairo_public cairo_surface_t *
cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device,
                                     cairo_content_t content,
                                     unsigned int tex,
                                     int width, int height);
cairo_public void
cairo_gl_surface_set_size (cairo_surface_t *surface, int width, int height);

cairo_public int
cairo_gl_surface_get_width (cairo_surface_t *abstract_surface);

cairo_public int
cairo_gl_surface_get_height (cairo_surface_t *abstract_surface);

cairo_public void
cairo_gl_surface_swapbuffers (cairo_surface_t *surface);

cairo_public void
cairo_gl_device_set_thread_aware (cairo_device_t        *device,
                                  cairo_bool_t           thread_aware);

cairo_public cairo_device_t *
cairo_egl_device_create (EGLDisplay dpy, EGLContext egl);

cairo_public cairo_surface_t *
cairo_gl_surface_create_for_egl (cairo_device_t *device,
                                 EGLSurface      egl,
                                 int             width,
                                 int             height);

cairo_public EGLDisplay
cairo_egl_device_get_display (cairo_device_t *device);

cairo_public EGLSurface
cairo_egl_device_get_context (cairo_device_t *device);