268 lines
9.5 KiB
Plaintext
268 lines
9.5 KiB
Plaintext
The FreeType 2 cache sub-system explained
|
|
(c) 2000 David Turner
|
|
|
|
-----------------------------------------------
|
|
|
|
Introduction :
|
|
--------------
|
|
|
|
this document describes the caching sub-system that comes
|
|
with the FreeType library, version 2.0. Note that unlike
|
|
the rest of the library, this code is still in beta stage
|
|
and might still suffer slight changes in the future.
|
|
|
|
Its basic design shouldn't evolve though and is explained
|
|
in this paper.
|
|
|
|
|
|
I. Requirements and Design Goals:
|
|
---------------------------------
|
|
|
|
The FT2 cache sub-system was designed to implement caching
|
|
of glyph images. However, it is extremely flexible and can
|
|
be easily extended to cache other kind of data like metrics,
|
|
character maps, coverage tables, etc..
|
|
|
|
|
|
II. Base Concepts:
|
|
------------------
|
|
|
|
1. The cache manager object:
|
|
|
|
at the heart of the caching sub-system is a single object
|
|
called the "cache manager". It is used to deal with FT_Face
|
|
and FT_Size objects, as well as to manager a LRU list of
|
|
abstract "cache nodes".
|
|
|
|
a. caching FT_Face and FT_Size objects:
|
|
|
|
each FT_Face object created by FreeType 2 can take from
|
|
a few hundred bytes to several tens of kilobytes, depending
|
|
on the original font's file format as well as its content.
|
|
|
|
there is no easy way to compute the size of a given FT_Face
|
|
object, so it's always a good idea to assume that it is
|
|
large and to want to limit the number of live face objects
|
|
as much as possible.
|
|
|
|
similarly, each FT_Face can have one or more FT_Size childs,
|
|
whose byte size depends heavily on the font format.
|
|
|
|
the first purpose of the cache manager is to provide a
|
|
small cache for FT_Face and FT_Size objects. Basically,
|
|
an application can use it as follows:
|
|
|
|
- each font face is described to the cache manager
|
|
through a typeless pointer, called a FTC_FaceID.
|
|
|
|
the cache manager itself doesn't interpret or use
|
|
the value of FTC_FaceIDs directly. Rather, it passes
|
|
them to a user-provided function called a
|
|
"face requester". see the defintion of the
|
|
FTC_Face_Requester type in <freetype/ftcache.h>
|
|
for details..
|
|
|
|
the face requester is in charge of translating a given
|
|
face into into a real new FT_Face object that is
|
|
returned to the cache manager. The latter will keep
|
|
the face object alive as long as it needs to.
|
|
|
|
the face requester is unique and must be passed
|
|
to the function named FTC_Manager_New used to
|
|
create/initialise a new cache manager.
|
|
|
|
|
|
- to lookup a given FT_Face, call the function
|
|
FTC_Manager_Lookup_Face as in the following code:
|
|
|
|
FTC_Manager_Lookup_Face( manager,
|
|
face_id,
|
|
&face );
|
|
|
|
if the corresponding FT_Face object is kept in
|
|
the cache manager's list, it will be returned
|
|
directly. Otherwise, this function will call
|
|
the user-provided face requester to create
|
|
a new FT_Face object, add it to the manager's
|
|
list to finally return it.
|
|
|
|
FT_Face objects are always destroyed by the cache
|
|
manager. An application that uses the cache
|
|
sub-system should never call FT_Done_Face !!
|
|
|
|
- to lookup a given FT_Size and FT_Face, call the
|
|
function FTC_Manager_Lookup_Size, as in:
|
|
|
|
FTC_Manager_Lookup_Size( manager,
|
|
ftc_font,
|
|
&face,
|
|
&size );
|
|
|
|
where "ftc_font" is a pointer to a FTC_Font descriptor
|
|
(a structure containing a FTC_FaceIDs and character
|
|
dimensions corresponding to the desired FT_Size).
|
|
|
|
note that the function returns both a FT_Face and
|
|
a FT_Size object. You don't need to call
|
|
FTC_Manager_Lookup_Face before it !!
|
|
|
|
also note that returned FT_Size objects are always
|
|
destroyed by the cache manager. A client application
|
|
that uses it should never call FT_Done_Size !!
|
|
|
|
|
|
the big advantage of using FTC_FaceIDs is that is
|
|
makes the caching sub-system completely independent
|
|
of the way font files are installed / listed / managed
|
|
in your application. In most implementations, a FTC_FaceID
|
|
is really a pointer to an application-specific structure
|
|
that describe the source font file + face index.
|
|
|
|
|
|
b - manage a MRU list of abstract "cache nodes":
|
|
|
|
the second role of the cache manager is to hold and manager
|
|
a list of abstract "cache nodes". The list is always sorted
|
|
in most-recently-used order. The manager always ensure that
|
|
the total size of nodes in memory doesn't over-reach a
|
|
certain threshold, by eliminating "old" nodes when
|
|
necessary.
|
|
|
|
the cache manager doesn't know much about the cache nodes:
|
|
|
|
- it knows how to move them in its list
|
|
- it knows how to destroy them when they're too old
|
|
- it knows how to "size" them (i.e. compute their byte
|
|
size in memory)
|
|
|
|
|
|
2. Cache objects:
|
|
|
|
the cache manager doesn't create new cache nodes however, this
|
|
is the charge of what are called "cache objects".
|
|
|
|
Basically, each cache object is in charge of managing cache
|
|
nodes of a certain type. Its role is to:
|
|
|
|
- provide a simple description of its cache nodes to the
|
|
manager (i.e. through a FTC_CacheNode_Class structure)
|
|
|
|
- provide a high-level API that can be called by client
|
|
applications to lookup cache nodes of the corresponding
|
|
type.
|
|
|
|
this function usually creates new nodes when they're not
|
|
available yet.
|
|
|
|
- also, and even though this is completely transparent to
|
|
the applications and the cache manager, each cache object
|
|
manages "node sets", where each set contains cache nodes
|
|
usually correspond to the same font face + font size.
|
|
|
|
|
|
For example, the cache sub-system currently comes with two
|
|
distinct cache classes:
|
|
|
|
- a FTC_Image_Cache, which is used to cache FT_Glyph images
|
|
(with one FT_Glyph per cache node).
|
|
|
|
|
|
- a FTC_SBit_Cache, which is used to cache small glyph bitmaps
|
|
("sbit" means "embedded bitmaps" in digital typography).
|
|
|
|
|
|
the small bitmaps glyph is useful because storing one glyph
|
|
image per cache node isn't memory efficient when the data
|
|
associated to each node is very small. Indeed, each cache
|
|
node has a minimal size of 20 bytes, which is huge when
|
|
your data is an 8x8 monochrome bitmap :-)
|
|
|
|
Hence, a FTC_SBit_Cache is capable of storing several
|
|
contiguous sbits in a single cache node, resulting in much
|
|
higher cached glyphs / total cache size.
|
|
|
|
an application can lookup a FT_Glyph image with a FTC_Image_Cache
|
|
by calling:
|
|
|
|
error = FTC_Image_Cache_Lookup( image_cache,
|
|
ftc_font,
|
|
glyph_index,
|
|
&ft_glyph );
|
|
|
|
or a FTC_SBit (small bitmap descriptor) by calling:
|
|
|
|
error = FTC_SBit_Cache_Lookup( sbit_cache,
|
|
ftc_font,
|
|
glyph_index,
|
|
&ftc_sbit );
|
|
|
|
III. Extending the cache sub-system:
|
|
|
|
It is possible to extend the current cache sub-system by
|
|
providing your own cache class and register it in the cache
|
|
manager. That might be useful to cache other kind of data
|
|
in the sub-system, like glyph metrics (without images),
|
|
|
|
To do it, you'll need to read the cache sub-system public
|
|
header files rather heavily :-) Fortunately, they're pretty
|
|
well commented and should guide you to your goal.
|
|
|
|
Note that the cache sub-system already provides two "abstract
|
|
cache" classes that can be re-used by your own implementation:
|
|
|
|
|
|
1. The abstract "FTC_GlyphCache" class:
|
|
|
|
this code is used to implement an abstract "glyph cache",
|
|
i.e. one that simply maps one glyph data per cache node.
|
|
|
|
it is sub-classed by FTC_Image_Cache, whose implementation
|
|
only consists in simple code to store a FT_Glyph in each
|
|
cache node.
|
|
|
|
you could sub-class it in your application to store glyph
|
|
images in a different format, for example.
|
|
|
|
see the files <freetype/cache/ftcglyph.h> and
|
|
"src/cache/ftcglyph.h" for details.
|
|
|
|
|
|
2. The abstract "FTC_ChunkCache" class:
|
|
|
|
this code is used to implement an abstract "glyph chunk cache".
|
|
it's very similar to a FTC_GlyphCache, except that it is capable
|
|
of storing several glyph-specific elements per cache node.
|
|
|
|
it is sub-classed by FTC_SBit_Cache, whose implementation
|
|
only consists in code to store a FTC_SBitRec record in each
|
|
node element.
|
|
|
|
you could sub-class it in your application to store small
|
|
glyph data, like metrics, glyph names, wathever.
|
|
|
|
see the files <freetype/cache/ftcchunk.h> and
|
|
"src/cache/ftcchunk.h" for details..
|
|
|
|
|
|
Note that the two abstract caches are rather complex because
|
|
they use "glyph sets". Each glyph set corresponds to a single
|
|
font face + font size combination. both caches are also
|
|
glyph-specific, though it is perfectly possible to use
|
|
broader selection criterion, here are a few examples:
|
|
|
|
- caching language coverage maps corresponding to
|
|
a given font face + language combination
|
|
|
|
- caching charmaps, layout tables, and other global
|
|
data..
|
|
|
|
- caching (font_face + font_size) specific "latin1"
|
|
ascender + descender
|
|
|
|
|
|
as you can see, a lot is possible with this design :-)
|
|
|
|
|
|
|
|
|