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.
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 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;
}
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);
}
}
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);
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.
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.
The following example cairo_window.c creates an EGL Wayland window, then a Cairo surface and draws "hello" in green on a yellow rectangle:
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);