The design of FreeType 2
Copyright 1998-2000 David Turner (
mailto:david@freetype.org
david@freetype.org
)
Copyright 2000 The FreeType Development Team (
mailto:devel@freetype.org
devel@freetype.org
)
design-3.html
Previous
index.html
Contents
design-5.html
Next
III. Internal Objects and Classes
Let us have a look now at the
internal
objects that
FreeType 2 uses, i.e., those not directly available to client
applications, and see how they fit into the picture.
1. Memory management
All memory management operations are performed through three specific
routines of the base layer, namely:
FT_Alloc()
,
FT_Realloc()
, and
FT_Free()
.  Each one of these
functions expects a
FT_Memory
handle as its first
parameter.
The latter is a pointer to a simple object used to describe the
current memory pool/manager.  It contains a simple table of
alloc/realloc/free functions.  A memory manager is created at library
initialization time by
FT_Init_FreeType()
, calling the function
FT_New_Memory()
provided by the
ftsystem
component.
By default, this manager uses the ANSI
malloc()
,
realloc()
, and
free()
functions.  However, as
ftsystem
is a replaceable part of the base layer, a specific
build of the library could provide a different default memory
manager.
Even with a default build, client applications are still able to
provide their own memory manager by not calling
FT_Init_FreeType()
but follow these simple steps:
Create a new
FT_Memory
object by hand.  The definition
of
FT_MemoryRec
is located in the public file
<freetype/ftsystem.h>
.
Call
FT_New_Library()
to create a new library instance
using your custom memory manager.  This new library doesn't yet
contain any registered modules.
Register the set of default modules by calling the function
FT_Add_Default_Modules()
provided by the
ftinit
component, or manually register your drivers by repeatedly
calling
FT_Add_Module()
.
2. Input streams
Font files are always read through
FT_Stream
objects.  The
definition of
FT_StreamRec
is located in the public file
<freetype/ftsystem.h>
, which allows client developers to
provide their own implementation of streams if they wish so.
The function
FT_New_Face()
will always automatically create
a new stream object from the C pathname given as its second
argument.  This is achieved by calling the function
FT_New_Stream()
provided by the
ftsystem
component.
As the latter is replaceable, the implementation of streams may vary
greatly between platforms.
As an example, the default implementation of streams is located in
the file
src/base/ftsystem.c
and uses the ANSI
fopen()
,
fseek()
, and
fread()
calls.
However, the Unix build of FreeType 2 provides an alternative
implementation that uses memory-mapped files, when available on the host
platform, resulting in a significant access speed-up.
FreeType distinguishes between memory-based and disk-based streams.
In the first case, all data is directly accessed in memory (e.g.
ROM-based, write-only static data and memory-mapped files), while in the
second, portions of the font files are read in chunks called
frames
, and temporarily buffered similarly through typical
seek/read operations.
The FreeType stream sub-system also implements extremely efficient
algorithms to very quickly load structures from font files while
ensuring complete safety in the case of a "broken file".
The function
FT_New_Memory_Face()
can be used to directly
create/open a
FT_Face
object from data that is readily
available in memory (including ROM-based fonts).
Finally, in the case where a custom input stream is needed, client
applications can use the function
FT_Open_Face()
, which can
accept custom input streams.  This may be useful in the case of
compressed or remote font files, or even embedded font files that need
to be extracted from certain documents.
Note that each face owns a single stream, which is also destroyed by
FT_Done_Face()
.  Generally speaking, it is certainly
not
a good idea to keep numerous
FT_Face
objects
opened.
3. Modules
A FreeType 2 module is itself a piece of code.  However, the
library creates a single
FT_Module
object for each module that
is registered when
FT_Add_Module()
is called.
The definition of
FT_ModuleRec
is not publicly available to
client applications.  However, each
module type
is described by
a simple public structure named
FT_Module_Class
, defined in
<freetype/ftmodule.h>
, and is described later in this
document:
You need a pointer to an
FT_Module_Class
structure when
calling
FT_Add_Module()
, whose declaration is:
FT_Error  FT_Add_Module( FT_Library              library,
const FT_Module_Class*  clazz );
Calling this function will do the following:
It will check whether the library already holds a module object
corresponding to the same module name as the one found in
FT_Module_Class
.
If this is the case, it will compare the module version number to
see whether it is possible to
upgrade
the module to a new
version.  If the module class's version number is smaller than the
already installed one, the function returns immediately.  Similarly,
it checks that the version of FreeType 2 that is running is
correct compared to the one required by the module.
It creates a new
FT_Module
object, using data and flags
of the module class to determine its byte size and how to properly
initialize it.
If a module initializer is present in the module class, it will
be called to complete the module object's initialization.
The new module is added to the library's list of "registered"
modules.  In case of an upgrade, the previous module object is
simply destroyed.
Note that this function doesn't return an
FT_Module
handle,
given that module objects are completely internal to the library (and
client applications shouldn't normally mess with them :-)
Finally, it is important to understand that FreeType 2
recognizes and manages several kinds of modules.  These will be
explained in more details later in this document, but we will list for
now the following types:
Renderer
modules are used to convert native glyph images
to bitmaps/pixmaps.  FreeType 2 comes with two renderer modules
by default: one to generate monochrome bitmaps, the other to
generate high-quality anti-aliased pixmaps.
Font driver
modules are used to support one or more font
formats.  Typically, each font driver provides a specific
implementation/derivative of
FT_Face
,
FT_Size
,
FT_GlyphSlot
, as well as
FT_CharMap
.
Helper
modules are shared by several font drivers.  For
example, the
sfnt
module parses and manages tables found in
SFNT-based font formats; it is then used by both the TrueType and
OpenType font drivers.
Finally, the
auto-hinter
module has a specific place in
the library's design, as its role is to process vectorial glyph
outlines, independently of their native font format, to produce
optimal results at small pixel sizes.
Note that every
FT_Face
object is
owned
by the
corresponding font driver, depending on the original font file's format.
This means that all face objects are destroyed when a module is
removed/unregistered from a library instance (typically by calling the
FT_Remove_Module()
function).
Because of this, you should always take care that no
FT_Face
object is opened when you upgrade or remove a module
from a library, as this could cause unexpected object deletion!
4. Libraries
We now come back to our well-known
FT_Library
object.  From
what have been said before, we already know that a library instance owns
at least the following:
A memory manager object (
FT_Memory
), used for all
allocation/releases within the instance.
A list of
FT_Module
objects, corresponding to the
"installed" or "registered" modules of the instance.  This list can
be changed at any time through
FT_Add_Module()
and
FT_Remove_Module()
.
Remember that face objects are owner by font drivers that are
themselves modules owned by the library.
There is however another object owned by the library instance that
hasn't been described yet: the
raster pool
.
The
raster pool
is simply a block of memory of fixed size
that is used internally as a "scratch area" for various memory-hungry
transient operations, avoiding memory allocation.  For example, it is
used by each renderer when converting a vectorial glyph outline into a
bitmap (actually, that's where its name comes from :-).
The size of the raster pool is fixed at initialisation time (it
defaults to 16kByte) and cannot be changed at run-time (though we could
fix this if there is a real need for that).
When a transient operation needs more memory than the pool's size, it
can decide to either allocate a heap block as an exceptional condition,
or sub-divide recursively the task to perform in order to never exceed
the pool's threshold.
This extremely memory-conservative behaviour is certainly one of the
keys to FreeType's performance in certain areas (most importantly in
glyph rendering/scanline-conversion).
5. Summary
Finally, the following picture illustrates what has been said in this
section, as well as the previous, by presenting the complete object
graph of FreeType 2's base design:
Complete library model
design-3.html
Previous
index.html
Contents
design-5.html
Next
