Glyph Keeper

Latest version: 0.32 (February 6, 2007)
[an error occurred while processing this directive] Home | [an error occurred while processing this directive] Introduction | [an error occurred while processing this directive] Screenshots | [an error occurred while processing this directive] Files | [an error occurred while processing this directive] Compiling | [an error occurred while processing this directive] Manual | [an error occurred while processing this directive] Reference | [an error occurred while processing this directive] Examples | [an error occurred while processing this directive] Future | [an error occurred while processing this directive] Thanks | [an error occurred while processing this directive] Links

User's Manual

Getting started

Before we start

First of all I suppose that you already succeeded compiling Glyph Keeper. You should have 'glyph.o' (or 'glyph.obj') file, don't forget to link it to your program. Also you should link FreeType and your target library - Allegro or SDL. (There is also a file IO target, intended for testing your Glyph Keeper / FreeType installation. It needs nothing more than 'stdio' for output.)

Linking your program

Please note, that libraries should be linked in specific order. If library 'a' depends on library 'b', 'a' should come first in the link command: "-la -lb". So when you link your program you should first list your program object files, then Glyph Keeper, then FreeType, then target (ex.Allegro) and system libraries.

Example command for linking your program, that uses Glyph Keeper and Allegro (MinGW):

gcc -o program.exe program.o -lglyph -lft -lalleg

Including Glyph Keeper header file

Every source file that will use Glyph Keeper's routines, should have #include "glyph.h" in the begining. It may be #include <glyph.h> if you already copied 'glyph.h' into your compiler's 'include' directory. Or it may be #include "glyph-keeper/include/glyph.h" if you placed Glyph Keeper files in 'glyph-keeper' directory.

Glyph Keeper supports several targets libraries (like Allegro or SDL), however only one of them must be used within one program. Target must be specified be defining a macro GLYPH_TARGET before including "glyph.h". If you use Allegro, add #define GLYPH_TARGET GLYPH_TARGET_ALLEGRO before including "glyph.h". It may be -DGLYPH_TARGET=GLYPH_TARGET_ALLEGRO in the compiler command line. For SDL do the same, just put GLYPH_TARGET_SDL instead.

Compatibility with older Glyph Keeper versions

Glyph Keeper is evolving and sometimes API changes are necessary, but the old API is usually still available through the bunch of #defines. There is a macro GLYPH_NO_LEGACY, that you can define in your program before including "glyph.h" to disable compatibility with the old messy API.

Checking Glyph Keeper version

glyph.h defines a few macros for this. For example, in Glyph Keeper version 0.21.3 they are:

#define GK_VERSION_MAJOR 0 #define GK_VERSION_MINOR 21 #define GK_VERSION_PATCH 3 #define GK_VERSION_STR "0.21.3" #define GK_DATE_STR "2005-02-07"

Initializing / Finalizing

Often you must call some 'init()' and 'done()' functions before and after using a library. Glyph Keeper does not require this. Initialization will happen when you first call Glyph Keeper routine. Cleanup code will be registered with atexit() and will be called at program shutdown. But if for some reason you need to control the order of things happening, you can do it with gk_library_init() and gk_library_cleanup().

Setting font search path

When you open a font from file, you provide a file name, and Glyph Keeper looks for this file in current directory and opens it. But sometimes it is convenient to keep font files in the same place, shared between multiple programs. Glyph Keeper lets you specify a font search path - list of directories where Glyph Keeper will look for a font file that you are trying to open. The font search path is set with gk_set_font_path() function.

You can set list of several directories, separate them with semicolon ';'. Always include the last slash character for each directory. For example, on Windows you may call

gk_set_font_path("C:\\fonts\\;C:\\WINDOWS\\Fonts\\");

After that when you try to load, for example, "times.ttf", it will be first searched in the current directory, then in C:\fonts, and finally in C:\WINDOWS\Fonts.

Font search path is empty by default. You can set it to 0 or to an empty string to make it empty again.

Loading a font from file

GLYPH_FACE *face = gk_load_face_from_file("arial.ttf",0); if (!face) { /* can't load -> complain, recover.. */ }

A pointer to GLYPH_FACE struct is used to refer to the loaded font face. You should always check returned value, 0 returned means failure.

Loading a font from a memory buffer

GLYPH_FACE *face2 = gk_load_face_from_memory(data,size,0);

This will load font face not from a file, but from memory location. It is helpful if you use some special way to distribute fonts with your program, for example if you compress them and pack together into a datafile.

Note, that you must not dispose 'data' during all time of using 'face2', or the next text output will crash.

Creating a renderer

In Glyph Keeper all text properties are organized in one structure – GLYPH_REND. Such properties include font face, font size, color, angle, transparency, rendering mode (monochrome or antialiased, hinted or not) and some more. Instead of specifying these parameters for every text output, with Glyph Keeper you set them up once in a renderer object, which you then use to render text with the same settings. Naturally, you can create many different renderers and use them in any order. Also, you can change any of the renderer's properties and continue using it. Creating a renderer:

GLYPH_REND *rend = gk_create_renderer(face,0); if (!rend) { /* can't create -> complain, recover.. */ }

Always check its return value, 0 means failure (although the only reason to fail may be out of memory). Created renderer is immediately ready to use, if you passed a valid FONT_FACE* as a first parameter. New renderer will have all settings set to default values, you will probably need to change size, color, etc.

Rendering text

Printing out a single character

When you are going to print out some text, all text properties should be already configured in a renderer object. Then only minimum information will be passed into text output function: a target bitmap, a renderer object, a text to print and coordinates:

gk_put_char(bmp,rend,'A',100,100);

This code will draw a capital 'A' on the bitmap pointed by 'bmp'. Left top corner of 'A' glyph will appear at the bitmap coordinates (100,100). Use gk_put_char_center() if you want a character centered at (100,100).

Rendering a single character

int x = 100<<6; int y = 100<<6; gk_render_char(bmp,rend,'A',&x,&y);

This prints a single 'A' to the bitmap. The difference from put_char() is that the coordinates x and y are passed by reference, and specify text origin point instead of left-top corner. After the character is printed out, the coordinates are changed to become the origin point of next character to be printed. This lets you print out a line of text by calling render_char() for each character in a line. Like this:

int x = 100<<6; int y = 100<<6; gk_render_char(bmp,rend,'H',&x,&y); gk_render_char(bmp,rend,'e',&x,&y); gk_render_char(bmp,rend,'l',&x,&y); gk_render_char(bmp,rend,'l',&x,&y); gk_render_char(bmp,rend,'o',&x,&y);

Please notice that "<<6", it is important. It means that 'x' and 'y' are expressed in 1/64th of a pixel The above code will start printing at (100,100) position, so it sets (x,y) to (100*64,100*64). (As you know 100<<6 is another way to say 100*64). If the coordinates were passed in integer pixels, a significant rounding error might have been accumulated during the angled text output. Whole text baseline might have drifted to the one side or another. Subpixel accuracy helps to avoid this problem.

Rendering a line of text

gk_render_line_utf8(bmp,rend,"Hello, World!",100,200);

This code will print "Hello, World!" to the bitmap 'bmp' with the font, color and other settings from 'rend'. The (100,200) point is used as text origin, similar to render_char(). This function expects coordinates in integer pixels, and it takes them as values, not pointers.

There are also UTF-16 and UTF-32 versions of this function. UTF-32 example:

unsigned text[] = {'I','b','a','n','e','z','!',0}; gk_render_line_utf32(bmp,rend,text,100,200);

Setting text properties

Setting font size

or

gk_rend_set_size_subpixel(rend,24<<6,24<<6);

This will set renderer's text size to 24x24 pixels. But what thing exactly becomes 24x24? It is the font's EM box, which historically means size of capital 'M'. These days it is not always the case, and fonts may look rather different at the same EM size.

You can set horisontal and vertical size independently. You may need it if you use graphics mode with non-square pixels, like 640x400, or just if you want letters to appear longer or wider than they really are.

Note, that text size is applied before text rotation. If you set text size to 24x40 (narrow letters), the letters will remain narrow in whatever angle you print them. It means that for angled text (printed along non-horisontal baseline) you can't workaround non-square pixels by setting special text size.

I will say again, to make clear. Currently angled text will look distorted in non proportional device resolutions, like 640x400 (unless your screen is really 640x400 in physical size). In future I may add independent setting for device resolution, so it will work fine. But for now you'd better avoid rendering angled text on non-proportional display.

Use gk_rend_set_size_subpixel() if you need subpixel accuracy in setting size.

Setting text color

You are probably used to specify text color each time you render text. This is how Allegro, AllegTTF and AllegroFont work. Glyph Keeper way is different - text color is specified once in the renderer, and then automatically used for each subsequent text output with that renderer.

gk_rend_set_text_color(rend,255,255,0);

This example will set text color to yellow (red = 255, green = 255, blue = 0). These color values are independent from your current color depth, they always are from 0 to 255. Black color is (0,0,0), white is (255,255,255), irrespective of the graphics mode you currently use. Of course, if you are rendering to the 8-bit bitmap, you may not see the exact color you specified in renderer - a nearest palette color will be used. But if later you use the same renderer with truecolor bitmap you get the right color.

Setting text transparency

Glyph Keeper can render transparent text, like in this example. Text letters are blended with the original image, so both can be seen. To enable transparent rendering you need to change renderer's alpha transparency setting - a number which spans from 0 to 255. 255 means opaque mode, no background will be visible, this is the default setting for every newly created renderer. 0 means actually no rendering is done, the background is left intact. Setting transparency to any number from 1 to 254 will enable transparent rendering:

Transparent rendering will work in any color depth except 8-bit. In 8-bit mode always opaque text will be produced. If you know what you are doing you will not want to render transparent text in 8-bit mode anyway. (But if you really need it you can first render it in some hicolor mode and then convert to 8-bit). For hi/true-color modes, 16-bit and 32-bit rendering is already optimized and is much faster than 15 or 24-bit.

Although you can do this, it's better not to render transparent text directly on screen or video bitmap. The reason is that transparent rendering involves both reading and writing to the bitmap, which is usually much slower for video RAM. Also, Glyph Keeper is not fully tested with different video hardware, banked VESA modes, X-modes and so on.

Setting color and transparency together

gk_rend_set_text_alpha_color(rend,0x9F01FF9C);

This line is taken from the example code. It sets transparency to 0x9F and color (r,g,b) to (0x01,0xFF,0x9C). Calling this function works a bit faster than setting transparency and color separately, because 0x9F01FF9C is exactly how it will be stored in the renderer structure anyway.

Using glyph cache

Introduction

Glyph cache is most important part of Glyph Keeper. It was the main purpose of writing it, and that's why it is named 'Glyph Keeper'. I was using other font libs and I once became frustrated about the way they did caching (among other things), if they did it at all. Then I started crafting some structures that would do what I wanted, and it ended up as a library.

Glyph Keeper's glyph cache is presented by a GLYPH_KEEP structure. That's it, glyph cache is a distinct entity, not bound to a specific renderer or font face. Cache object exists on its own, providing caching facility to one or more renderers. This works even if renderers have different font faces and sizes, so the whole program can be served by a single cache object (it's even not a bad idea to use only one cache object).

Creating cache object

GLYPH_KEEP *keep; keep = gk_create_keeper(1000,2*1024*1024); if (!keep) { /* complain, recover */ )

When you create a glyph cache, you specify its maximum storage capacity: maximum number of glyphs it can store and maximum memory space it can use. In this example a cache is created with the limit of 1000 stored glyphs and 2 MB of maximum memory usage. It means that whenever a new glyph is going to be stored in cache, and <number of currently stored glyphs is 1000> OR <cache memory consumption would exceed 2 MB after that new glyph is stored>, in such case some (one or more) of least recently used cached glyphs will be first deleted from a cache, until it becomes possible to add a new glyph to the cache without exceeding the limits.

If you pass 0 in place of glyph number limit, there will be no upper limit for number of stored glyphs. If you pass 0 as memory limit, there will be no limit for occupied memory. Depending on the pattern of text rendering usage of your program, you may choose limiting only glyph number, only memory usage, or using both limits of a glyph cache (or even having cache with no limitations). Beware, that if you use only glyph count limit, glyph cache memory usage may grow very large, if you start using larger font sizes. So, for a common case usage I recommend having memory-limited cache, with unlimited number of glyphs:

keep = gk_create_keeper(0,2*1024*1024);

Conecting a glyph cache to a renderer

As you know, every aspect of text rendering is specified in a renderer object (GLYPH_REND structure). This includes glyph caching. Easiest way of setting a renderer to use cache is to provide a cache object when creating a renderer:

GLYPH_REND *rend = gk_create_renderer(face,keep);

Another way is to attach a cache to already created renderer:

gk_rend_set_keeper(rend,keep);

Once a renderer has been set to use cache, all glyphs it is asked to render are first looked up in cache. If the glyph is found it is returned to the drawing routine immediately, otherwise it is rendered, added into the cache, and returned. Glyphs are arranged in cache in most recently used order - whenever a glyph is successfully found in cache it is moved to the begining of the list.

A single cache object may be used with several renderers. In such case all glyphs are arranged in a single most-recently-used ordered list. If one of the renderer is starting to be used a lot, it may drive other renderer's glyphs out of cache.

If you need you can always return a renderer into its original un-cached mode:

Precaching

Although all rendered glyphs are automatically placed into cache (if the renderer is connected to cache object), sometimes you may prefer to put glyphs into the cache manually. This may be useful if you expect a CPU-intensive work soon, that will also require some text rendering. In such case you can prepare the character glyphs beforehand, so that when needed they are simply taken out of cache.

There are several functions doing precaching, it is best to show them by example. The following 5 code fragments all do the same thing: prepare glyphs for characters 'a', 'b', 'c', 'd' and 'e' and put them into glyph cache:

  1. gk_precache_char(rend,'a'); gk_precache_char(rend,'b'); gk_precache_char(rend,'c'); gk_precache_char(rend,'d'); gk_precache_char(rend,'e');
  2. gk_precache_range(rend,'a','e');
  3. const char* char_set = "abcde"; gk_precache_set_utf8(rend,char_set);
  4. const unsigned short[] char_set = {'a','b','c','d','e',0}; gk_precache_set_utf16(rend,char_set);
  5. const unsigned[] char_set = {'a','b','c','d','e',0}; gk_precache_set_utf32(rend,char_set);

Checking cache state

You can check current cache limits:

int glyph_count_limit = gk_keeper_glyph_limit(keep); int memory_limit = gk_keeper_byte_limit(keep);

Checking current number of cached glyphs and amount of memory currently used by cache:

int glyph_count = gk_keeper_glyph_count(keep); int memory_used = gk_keeper_byte_count(keep);

Disposing glyph cache

Normally you don't have to care about it - glyph caches will be disposed automatically at the shutdown time of your program, or when you call glyph_keeper_library_done() manually. But I can imagine situations when a program suddenly needs to use all available memory, still keeping capability to render text. In such case you can safely dispose a glyph cache:

All cached glyphs and index tables of cache object 'keep' will be removed from the memory. All renderers that were using that cache object will be automatically switched to work without cache.

Character remapping

Introduction

Imagine that the font you use lacks some characters. Should you modify the font? What if font's license doesn't allow it, or if it's a standard system font that you don't distribute? Perhaps those missing characters can be found in some other font, that you can distribute with your program, or another one of system fonts (or in a handmade font you made just for those characters). Now how to combine these two fonts together? You will need to add some logic to all your text output, to use first font for all normal text output and second font for those characters.

Since version 0.23 Glyph Keeper supports character remapping. Its lets you combine several fonts together without altering the font files and without changing the way you output text. For example you may have one nice font for Latin script, another font for CJK (Chinese-Japanese-Korean) characters and another one for Cyrillic. Your program, after you set the character remapping, may read text from file or network, and immediately render it to screen. The proper font will be selected automatically.

Remapping a single character

Remapping is done with GLYPH_FACE objects. This example remaps an asterisk character (U+002A) of "face1" to the same character from another font face:

gk_remap_character(face1,'*',face2,'*');

After this call all renderers (GLYPH_REND objects) using face1 will render asterisk of face2, althouth all other text will still be in face1. Note, that it does not matter whether face1 has its own asterisk character, in any case from now on face1's asterisk will appear as face2's one. You can later override the remapping by calling this function again, for example:

gk_remap_character(face1,'*',face2,0x2217);

will map asterisk (U+002A) of face1 to "asterisk operator" (U+2217) of face2. You can cancel a remapping by either

gk_remap_character(face1,'*',0,0);

or

gk_unmap_character(face1,'*');

(these two lines are equivalent). After this the default behavior is restored. It means original face1's asterisk will be used, or "undefined character", if asterisk is missing in face1.

Remapping a range of characters

gk_remap_range(face1,'A',face2,'a',26);

This example will make all uppercase latin characters of face1 appear as lowercase characters of face2. To removing range remapping:

gk_remap_range(face1,'A',0,0,26);

or equivalent

gk_unmap_range(face1,'A',26);

Allegro-specific usage of Glyph Keeper

Allegro built-in text rendering

Allegro has built-in font loading and text rendering routines. They are limited to bitmap fonts, so angled or italicized text can't be produced (as of Allegro 4.2.1). Also it can't adjust font size and boldness.

Allegro defines a FONT structure, where it keeps the font data. A FONT must be specified for every Allegro's text rendering function, so it is analogous to Glyph Keeper's GLYPH_REND structure. Allegro provides some convenient text rendering functions, including a printf-style formatted text output. Also Allegro text rendering functions take text in current encoding, which may be set to UTF-8. (Allegro does not support UTF-16 or UTF-32)

Creating a FONT stucture with Glyph Keeper

Since version 0.20 Glyph Keeper can produce a FONT objects, that can be used with Allegro's text rendering functions. FONT objects created with Glyph Keeper, can render text with all features - scalable, antialiased, hinted, transparent, angled and oblique rendering is possible. In this way any existing program, even it it has thousands of calls to Allegro text rendering functions, can be converted to take advantage of high quality text rendering with just a few lines of code.

A FONT object can be created for a given renderer - a GLYPH_REND object:

FONT* font1 = gk_create_allegro_font(rend1);

New FONT object 'font1' is created for this specific renderer - 'rend1'. Now any text rendered using 'font1' with Allegro functions will have same appearance as if it was rendered with 'rend1' and Glyph Keeper's functions. Also, you may change any the renderer's settings and the FONT object will immediately produce text with these new settings.

So, at first you will need set up all you need with Glyph Keeper - load a font faces from file, create glyph cache, create renderers, configure renderers to produce text you like. Then you create a FONT objects for each of the renderers, and you just use them and forget about Glyph Keeper for the rest of your program (ideally, at least).

2005-01-26: Allegro 4.1.18 changed the font vtable, so if you use Glyph Keeper to produce Allegro FONT objects, and you have Allegro 4.1.18, you need to upgrade to Glyph Keeper 0.20.1 or later.

What if you will try to produce two FONT objects for the same renderer? Nothing special. A second time no new FONT object will be created, just the pointer to the first one will be returned to you. It will be like just copying the same address value to the second variable. The renderer keeps a link back to its FONT object. Also, When the renderer is destroyed, it takes FONT object with him.

Destroying Glyph Keeper-generated FONT object

In short, you don't have to care about it. It will be disposed automatically at the end of your program run, or when you call gk_library_cleanup(). Also, the FONT object will be gone when you dispose the renderer with gk_done_renderer(). If you need you can call Allegro's destroy_font() and the FONT object will be gone. In any case, don't try to use a FONT object, which was disposed by any of the mentioned ways.

Text height issue

Allegro font routines are designed to work with bitmap fonts - fonts where every character glyph is explicitly specified in a font file. This has several consequences on Allegro text rendering: 1. Text height can be known for a font. With bitmap fonts it is possible to define a text height measure and to actually find it for a given font. This is, of course, convenient for a text layout, when you need to know how large space a line of text can occupy. 2. As a consequence, for every text output Allegro routines take coordinates of the left-top corner of a text rectangle to be rendered. For example if you specify (20,20) you know that the text will appear lower and to the left from (20,20). This is very convenient and easy way to think about text output, as long as you work with bitmap fonts.

Glyph Keeper works with scalable fonts, which makes things more complex. Although ascender and descender values are specified in the font file, the exact height of a scalable font can not be found reliably for several reasons: 1. The ascender and descender values stored in a font file are sometimes erroneous. 2. The font may contain thousands of characters, checking the height of each of them may take some time. 3. Hinting process can change the shape of a character, making it a bit higher or lower. 4. In complex text rendering an accent marks or some other combined character may be attached to the base character, making it higher (This is not supported by current Glyph Keeper, but I hope to do this in future).

So, when you switch to the FONT objects generated by Glyph Keeper, you should be careful about text height. Allegro's text_height() will still give you the estimation of a font height, but you can't completely rely on this value. Sometimes it may be possible that a character is a little higher, suddenly. Such character may extend beyond the expected text rectangle, crossing to the lower or upper border. Incorrect ascender and descender values, or enabled hinting may also result in this problem. If you want to prevent anything jumping out of your text box, you may use bitmap clipping.

You can use FONT object for angled (non-horizontal) text rendering too. Just change the angle in the renderer structure, and continue using allegro text rendering functions. But be aware, that in that case the Allegro's text_height() will still return the height of horizontal text, calculated as (ascender - descender).

To be continued...
Valid HTML 4.01! Valid CSS! Open Source
Against TCPA No software patents No software patents