summaryrefslogtreecommitdiff
path: root/posts/pangocairo.md
diff options
context:
space:
mode:
Diffstat (limited to 'posts/pangocairo.md')
-rw-r--r--posts/pangocairo.md321
1 files changed, 0 insertions, 321 deletions
diff --git a/posts/pangocairo.md b/posts/pangocairo.md
deleted file mode 100644
index fd651b5..0000000
--- a/posts/pangocairo.md
+++ /dev/null
@@ -1,321 +0,0 @@
-title: Font Rendering in OpenGL with Pango and Cairo
-date: 2013-08-17 16:00:00
-tags: opengl, pango, cairo, font, wsu
-summary: A brief tutorial for rendering fonts in OpenGL with libpangocairo
----
-
-I am working towards a 0.1 release of my game development framework
-for GNU Guile, [guile-2d](https://github.com/davexunit/guile-2d). One
-of the few remaining blockers on my to-do list is font rendering. A
-reddit user, [Madsy9](http://www.reddit.com/user/Madsy9), pointed me
-in the right direction with this
-[comment](http://www.reddit.com/r/scheme/comments/1k739l/guile_2d_game_programming_lib_for_scheme/cbmnyuk). There
-are two libraries needed to perform nice font rendering with proper
-internationalization support: [Pango](http://www.pango.org/), “a
-library for laying out and rendering of text, with an emphasis on
-internationalization,” and [Cairo](http://cairographics.org/), “a 2D
-graphics library with support for multiple output devices.”
-
-It took me awhile to put together all of the pieces and build a
-working sample program. The goal of this post is to help others that
-may be trying to accomplish a similar task that have no prior
-knowledge of Pango and Cairo. I will assume basic knowledge of C, SDL,
-and OpenGL throughout this post.
-
-Let’s get the basic SDL and OpenGL initialization out of the way:
-
-```c
-#include <pango/pangocairo.h>
-#include <SDL.h>
-#include <SDL_opengl.h>
-
-#define WINDOW_WIDTH 800
-#define WINDOW_HEIGHT 600
-#define FONT "Sans Bold 18"
-#define TEXT "The quick brown fox is so かわいい!"
-
-void
-init_sdl ()
-{
- SDL_Init (SDL_INIT_EVERYTHING);
- SDL_SetVideoMode (WINDOW_WIDTH, WINDOW_HEIGHT, 0, SDL_OPENGL);
-}
-
-void
-init_gl ()
-{
- glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
- glDisable (GL_DEPTH_TEST);
- glEnable (GL_BLEND);
- glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable (GL_TEXTURE_2D);
- glViewport (0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
- glMatrixMode (GL_PROJECTION);
- glLoadIdentity ();
- glOrtho (0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, -1, 1);
- glMatrixMode (GL_MODELVIEW);
- glLoadIdentity ();
-}
-```
-
-`create_texture` simply creates an OpenGL texture given an array of
-pixel data and the texture dimensions. Our Cairo surface will use BGRA
-color.
-
-```c
-unsigned int
-create_texture (unsigned int width,
- unsigned int height,
- unsigned char *pixels)
-{
- unsigned int texture_id;
-
- glGenTextures (1, &texture_id);
- glBindTexture (GL_TEXTURE_2D, texture_id);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexImage2D (GL_TEXTURE_2D,
- 0,
- GL_RGBA,
- width,
- height,
- 0,
- GL_BGRA,
- GL_UNSIGNED_BYTE,
- pixels);
-
- return texture_id;
-}
-```
-
-`draw_texture` clears the screen, renders a simple textured quad using
-OpenGL’s immediate mode, and then swaps buffers.
-
-```c
-void
-draw_texture (int width,
- int height,
- unsigned int texture_id)
-{
- /* Render a texture in immediate mode. */
- glMatrixMode (GL_MODELVIEW);
- glLoadIdentity ();
- glClear (GL_COLOR_BUFFER_BIT);
- glPushMatrix ();
- glBindTexture (GL_TEXTURE_2D, texture_id);
- glColor3f (1.f, 1.0f, 1.0f);
-
- glBegin (GL_QUADS);
- glTexCoord2f (0.0f, 0.0f);
- glVertex2f (0.0f, 0.0f);
- glTexCoord2f (1.0f, 0.0f);
- glVertex2f (width, 0.0f);
- glTexCoord2f (1.0f, 1.0f);
- glVertex2f (width , height);
- glTexCoord2f (0.0f, 1.0f);
- glVertex2f (0.0f, height);
- glEnd ();
-
- glPopMatrix ();
- SDL_GL_SwapBuffers();
-}
-```
-
-`create_cairo_context` is used to make a new Cairo context that draws
-to a raw data surface. The return value, a `cairo_t`, is the main
-object in Cairo. All drawing is done via a `cairo_t` object. A
-context needs a surface to draw on.
-`cairo_image_surface_create_for_data` creates a raw data surface for
-us. We will be translating the surface into a texture later on.
-
-```c
-cairo_t*
-create_cairo_context (int width,
- int height,
- int channels,
- cairo_surface_t** surf,
- unsigned char** buffer)
-{
- *buffer = calloc (channels * width * height, sizeof (unsigned char));
- *surf = cairo_image_surface_create_for_data (*buffer,
- CAIRO_FORMAT_ARGB32,
- width,
- height,
- channels * width);
- return cairo_create (*surf);
-}
-```
-
-`create_layout_context` also makes a new Cairo context, but this
-context is for PangoLayout objects. In Pango, a layout describes the
-style of a paragraph of text. The layout needs a context in order to
-function. We use `cairo_image_surface_create` with dimensions of 0x0
-because we won’t actually be rendering to this surface. Instead, we
-will layout our text and use `create_cairo_context` to build a context
-with a surface that is the size of the rendered text. Cairo uses
-reference counting for dynamically allocated objects, so we need to
-call `cairo_surface_destroy` when we’re done with the temporary
-surface. The context still maintains a reference to the surface, so
-the memory for the surface will not be freed until the context is.
-
-```c
-cairo_t*
-create_layout_context ()
-{
- cairo_surface_t *temp_surface;
- cairo_t *context;
-
- temp_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
- context = cairo_create (temp_surface);
- cairo_surface_destroy (temp_surface);
-
- return context;
-}
-```
-
-`get_text_size` tells us the size of the text that’s in the layout, in
-pixels. Pango’s units are not in pixels, so we must divide by
-`PANGO_SCALE` in order to get pixel units.
-
-```c
-
-void
-get_text_size (PangoLayout *layout,
- unsigned int *width,
- unsigned int *height)
-{
- pango_layout_get_size (layout, width, height);
- /* Divide by pango scale to get dimensions in pixels. */
- *width /= PANGO_SCALE;
- *height /= PANGO_SCALE;
-}
-```
-
-`render_text` is where all of the magic happens. First, we create a
-layout with a layout context and set the text that we will render with
-this layout. `TEXT` is defined earlier in the program as "The quick
-brown fox is so かわいい!"
-
-Then we create a `PangoFontDescription` object. This object
-represents the font that we want to render. Earlier in the program,
-`FONT` is defined as "Sans Bold 18". Pango is able to figure out how
-to load a font from a string in this format. Your system must be able
-to recognize the font family and font face, though. I haven’t yet
-figured out how to have Pango render an arbitrary font from a `*.ttf`
-file.
-
-Next, we create a rendering context by getting the layout’s size and
-creating a context with a surface big enough to show all of the
-rendered text.
-
-Finally, we set the font color to white, render the text to the
-surface with `pango_cairo_show_layout`, and create an OpenGL texture
-from the surface. We also clean up all the objects that we no longer
-need before returning.
-
-```c
-unsigned int
-render_text (const char *text,
- unsigned int *text_width,
- unsigned int *text_height,
- unsigned int *texture_id)
-{
- cairo_t *layout_context;
- cairo_t *render_context;
- cairo_surface_t *temp_surface;
- cairo_surface_t *surface;
- unsigned char* surface_data = NULL;
- PangoFontDescription *desc;
- PangoLayout *layout;
-
- layout_context = create_layout_context ();
-
- /* Create a PangoLayout, set the font and text */
- layout = pango_cairo_create_layout (layout_context);
- pango_layout_set_text (layout, text, -1);
-
- /* Load the font */
- desc = pango_font_description_from_string (FONT);
- pango_layout_set_font_description (layout, desc);
- pango_font_description_free (desc);
-
- /* Get text dimensions and create a context to render to */
- get_text_size (layout, text_width, text_height);
- render_context = create_cairo_context (*text_width,
- *text_height,
- 4,
- &surface,
- &surface_data);
-
- /* Render */
- cairo_set_source_rgba (render_context, 1, 1, 1, 1);
- pango_cairo_show_layout (render_context, layout);
- *texture_id = create_texture(*text_width, *text_height, surface_data);
-
- /* Clean up */
- free (surface_data);
- g_object_unref (layout);
- cairo_destroy (layout_context);
- cairo_destroy (render_context);
- cairo_surface_destroy (surface);
-}
-```
-
-`main` is pretty simple. We initialize SDL and OpenGL, render text
-to a texture, and enter the rendering loop. The program will run
-until you click the close button, press "enter", or press "q".
-
-```c
-int main (int argc, char **argv)
-{
- SDL_Event event;
- int keep_running = 1;
- unsigned int texture_id;
- unsigned int text_width = 0;
- unsigned int text_height = 0;
-
- init_sdl ();
- init_gl ();
- render_text(TEXT,
- &texture_id,
- &text_width,
- &text_height);
-
- /* Update/render loop */
- while (keep_running) {
- SDL_PollEvent (&event);
-
- switch (event.type) {
- case SDL_QUIT :
- keep_running = 0;
- break;
-
- case SDL_KEYDOWN :
- if (event.key.keysym.sym == SDLK_ESCAPE)
- keep_running = 0;
- if (event.key.keysym.sym == SDLK_q)
- keep_running = 0;
- break;
- }
-
- draw_texture (texture_id, text_width, text_height);
- SDL_Delay (16);
- }
-
- /* Clean up */
- glDeleteTextures (1, &texture_id);
-
- SDL_Quit();
-
- return 0;
-}
-```
-
-And we’re done! You should now be able to render some text in an
-OpenGL context. I hope this brief tutorial was helpful. Font rendering
-isn’t easy, and it’s not really my area of interest. I’m glad that
-Pango exists to do all of the real work for me so that I can more
-quickly move on to the parts of graphics programming that I actually
-enjoy.
-
-You can download the full source code [here](/src/pangocairo.tar.gz).