FreeType 2.0 System Interface

© 2000 David Turner (david@freetype.org)
© 2000 The FreeType Development Team (devel@freetype.org)


Introduction:

    This document explains how the FreeType 2 library performs the low-level and system-specific operations of memory management and i/o access. It is targetted to FreeType hackers, porters and "advanced" developers who want special features like providing their own memory manager or streams.

    Note that the only system-specific part of the library is a file named "ftsystem.c". Its default implementation is in the directory "freetype2/src/base", even though platform-specific builds may contain a system-specific version in a directory like "freetype2/builds/system".


I. Memory Management

    Memory allocation and releases are performed through a FT_Memory object in FreeType. A FT_Memory is nothing more than a table of functions plus an arbitrary user data field. It is defined in the file "freetype2/include/ftsystem.h" and has the following structure:

      typedef struct
      {
        void* user // a user-defined pointer. This is zero by default
        void* (*alloc)( FT_System, int) // a function used to allocate a new block
        void* (*realloc)( FT_System, int, int, void* ) // a function used to reallocate a given block
        void (*free)( FT_System, void*) // a function used to release a given block
      } FT_MemoryRec, *FT_Memory;

    You'll notice that:

    • The FT_Memory type is really a pointer to a FT_MemoryRec. This is a normal convention for the FreeType code.
    • The realloc takes two integer arguments. The first one is the current block size, the second one its new size.

    All current implementations of "ftsystem.c" provide a very simple implementation of the FT_Memory interface by calling directly the standard C alloc, realloc and free.

    The FreeType source code never invokes directly the function pointers. Rather, it calls FT_Alloc, FT_Realloc and FT_Free functions which are defined in "freetype2/src/base/ftobjs.c". These will not be discussed here.

    If you want to use your own memory allocator rather than the one provided by your build of FreeType, follow these simple steps:

    1. Create your own FT_Memory object, with pointers that map to your own memory management routines (beware function signatures though).

    2. Call FT_Build_Library(memory,&library). This will create a new FT_Library object that uses your own FT_Memory exclusively. Note however that this library has no font drivers loaded in !!

    3. Load the default font drivers into the new library, either by calling FT_Default_Drivers(library), or by adding them manually through repeated calls to FT_Add_Driver(library,&driver_interface)

    This will replace the FT_Init_FreeType(&library) call that an application must do to initialise one library instance.

    Notice that you don't need to recompile FreeType 2 to use your own memory manager !!.


II. Streams

    1. Basic Stream Structure

    A stream models the array of bytes found in a font file. FreeType 2 separates streams into two families :

    • memory-based streams:
      when the stream's content is entirely found in memory. This is the case for ROM font files, or memory-mapped files.

    • disk-based streams:
      when the stream isn't directly accessible in memory. This is the case for local or remote files.

    Note that a stream's nature only determines how FreeType accesses its content, not the way it is effectively stored. For example, in the case of a compressed font file, one implementation may choose to uncompress the font in memory, then provide a memory based stream to access it. Another one might chose a disk based stream to perform on-the-fly decompression of the font data. Similarly, the font file can be stored on a local disk, or obtained from a network. This will be completely transparent to FreeType.

    The stream structure is:

      typedef struct
      {
        char* base for memory-based streams, the address of its first byte.
        ulong size the stream's size in bytes.
        ulong pos the current stream position in the file
        descriptor a union field used to hold either an integer file descriptor or pointer. This field is not used by FreeType itself, but is left to implementations of "ftsystem"
        pathname a union field that can hold either an integer or pointer. It is not used by FreeType itself, but is left to implementations. These can put the file pathname's during debugging for example.
        read a pointer to a function used to seek the stream and/or read a run of bytes from it.
        close a pointer to a function called when the stream is closed.
        memory a FT_Memory object, which is used to allocate frames for disk-based streams. This field is set and used by FreeType.
        cursor a pointer in memory used when accessing frames. This is set and used by FreeType.
        limit a pointer in memory used when accessing frames. This is set and used by FreeType.
      } FT_StreamRec, *FT_Stream

    The following important things must be noticed here:

    • The FT_Stream type is really a pointer to a FT_StreamRec. This is a normal convention for the FreeType source.

    • When the read field is non NULL, the stream is considered to be disk-based. Otherwise, the stream is memory-based, and the base field must be set by "ftsystem.c" when the stream is created.

    • The base field must be set to 0 when a disk-based stream is created. However, this field will later be set and used by the FreeType library when accessing frames of bytes within the font file (of course, this doesn't happen with memory-based streams).

    2. Stream lifecyles

    Each FT_Face needs its own stream to access font data. The most common way to create a new FT_Stream object is to call the function FT_New_Face. This function takes a file pathname argument that is used to create a new stream object.

    This is possible because each implementation of "ftsystem.c" provides a function called FT_New_Stream which takes a file pathname and a FT_Stream pointer as an argument. The function simply opens the file and initialises the stream structure accordingly. It is called by FT_New_Face to create the face's stream object.

    A stream is only closed when the face is destroyed through FT_Done_Face. Its close field function will then be called. Note that the function should never destroy the FT_Stream.

    3. Using your own streams

    There are cases where it is interesting to provide your own stream to create a new face object, rather than rely on the default implementation. For example, a filepathname, which is a C string, might not be useful on a system where files are named with a UTF-16 string or via an i-node number of memory address (for ROM files).

    For this purpose, the FT_Open_Face is defined. It simply takes a FT_Stream pointer as its second argument, instead of a file pathname (the stream must be allocated and initialised by you, so be careful).

    Actually, the only thing that FT_New_Face does is create a new stream through FT_New_Stream, then call FT_Open_Face to create the face with it.

    Note also that you can use the function FT_New_Memory_Face to create a new font face for a memory-based font file, whose address and size can be passed as arguments. The function automatically creates the corresponding memory-based stream and use it to create the face.


III. Thread synchronisation

    The FreeType library uses no static data. It can be used concurrently by two thread as long as each one uses its own FT_Library instance. Otherwise, one can very simply synchronize access to a single library instance by using a mutex to protect each call to one of FreeType's API functions.