diff options
Diffstat (limited to 'posts/2013-08-17-font-rendering-pango-cairo.skr')
-rw-r--r-- | posts/2013-08-17-font-rendering-pango-cairo.skr | 328 |
1 files changed, 0 insertions, 328 deletions
diff --git a/posts/2013-08-17-font-rendering-pango-cairo.skr b/posts/2013-08-17-font-rendering-pango-cairo.skr deleted file mode 100644 index 59e0f96..0000000 --- a/posts/2013-08-17-font-rendering-pango-cairo.skr +++ /dev/null @@ -1,328 +0,0 @@ -(define guile-2d-url "https://github.com/davexunit/guile-2d") -(define madsy9-url "http://www.reddit.com/user/Madsy9") -(define madsy9-comment-url - "http://www.reddit.com/r/scheme/comments/1k739l/guile_2d_game_programming_lib_for_scheme/cbmnyuk") -(define pango-url "http://www.pango.org/") -(define cairo-url "http://cairographics.org/") -;; TODO: Move to files.dthompson.us -(define tarball-url "/src/pangocairo.tar.gz") - -(post - :title "Font Rendering in OpenGL with Pango and Cairo" - :date (make-date* 2013 08 17) - :tags '("opengl" "pango" "cairo" "font" "wsu") - :summary "A brief tutorial for rendering fonts in OpenGL with libpangocairo" - - (p [I am working towards a 0.1 release of my game development - framework for GNU Guile, ,(anchor "guile-2d" guile-2d-url). One of - the few remaining blockers on my to-do list is font rendering. A - reddit user, ,(anchor "Madsy9" madsy9-url), pointed me in the right - direction with this ,(anchor "comment" madsy9-comment-url). There are - two libraries needed to perform nice font rendering with proper - internationalization support: ,(anchor "Pango" pango-url), “a library - for laying out and rendering of text, with an emphasis on - internationalization,” and ,(anchor "Cairo" cairo-url), “a 2D graphics - library with support for multiple output devices.”]) - - (p [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.]) - - (p [Let's get the basic SDL and OpenGL initialization out of the way:]) - - (source-code - (c-source - "#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 (); -}")) - - (p [,(code "create_texture") simply creates an OpenGL texture given an array of - pixel data and the texture dimensions. Our Cairo surface will use BGRA - color.]) - - (source-code - (c-source - "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; -}")) - - (p [,(code "draw_texture") clears the screen, renders a simple textured quad - using OpenGL’s immediate mode, and then swaps buffers.]) - - (source-code - (c-source - "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(); -}")) - - (p [,(code [create_cairo_context]) is used to make a new Cairo context that -draws to a raw data surface. The return value, a ,(code [cairo_t]), is the -main object in Cairo. All drawing is done via a ,(code [cairo_t]) object. A -context needs a surface to draw on. -,(code [cairo_image_surface_create_for_data]) creates a raw data surface for -us. We will be translating the surface into a texture later on.]) - - (source-code - (c-source - "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); -}")) - - (p [,(code [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 ,(code [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 ,(code [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 ,(code [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.]) - - (source-code - (c-source - "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; -}")) - - (p [,(code [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 -,(code [PANGO_SCALE]) in order to get pixel units.]) - - (source-code - (c-source - "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; -}")) - - (p [,(code [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. ,(code [TEXT]) is defined earlier in the program as “The quick -brown fox is so かわいい!”]) - - (p [Then we create a ,(code [PangoFontDescription]) object. This object -represents the font that we want to render. Earlier in the program, -,(code [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 -,(code [*.ttf]) file.]) - - (p [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.]) - - (p [Finally, we set the font color to white, render the text to the -surface with ,(code [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.]) - - (source-code - (c-source - "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); -}")) - - (p [,(code [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”.]) - - (source-code - (c-source - "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; -}")) - - (p [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.]) - - (p [You can download the full source code ,(anchor [here] tarball-url).])) |