Official mirror of https://gitlab.freedesktop.org/freetype/freetype
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
710 lines
23 KiB
710 lines
23 KiB
|
|
Conventions and Design in the FreeType 2 library |
|
------------------------------------------------ |
|
|
|
|
|
Table of Contents |
|
|
|
Introduction |
|
|
|
I. Style and Formatting |
|
|
|
1. Naming |
|
2. Declarations & Statements |
|
3. Blocks |
|
4. Macros |
|
5. Conventions |
|
|
|
II. Design conventions |
|
|
|
1. Modularity and Components Layout |
|
2. Configuration and Debugging |
|
|
|
III. Usage conventions |
|
|
|
1. Error handling |
|
2. Font File I/O |
|
3. Memory management |
|
4. Support for threaded environments |
|
5. Object Management |
|
|
|
|
|
|
|
Introduction |
|
============ |
|
|
|
This text introduces the many conventions used within the FreeType 2 |
|
library code. Please read it before trying any modifications or |
|
extensions of the source code. |
|
|
|
|
|
|
|
I. Style and Formatting |
|
======================= |
|
|
|
The following coding rules are extremely important to keep the |
|
library's source code homogeneously. Keep in mind the following |
|
points: |
|
|
|
- `Humans read source code, not machines' (Donald Knuth) |
|
|
|
The library source code should be as readable as possible, even |
|
by non-C experts. With `readable', two things are meant: First, |
|
the source code should be pleasant to the eye, with sufficient |
|
whitespace and newlines, to not look like a boring stack of |
|
characters stuck to each other. Second, the source should be |
|
_expressive_ enough about its goals. This convention contains |
|
rules that can help the source focus on its purpose, not on a |
|
particular implementation. |
|
|
|
- `Paper is the _ultimate_ debugger' (David Turner :-) |
|
|
|
There is nothing like sheets of paper (and a large floor) to |
|
help you understand the design of a library you're new to, or to |
|
debug it. The formatting style presented here is targeted at |
|
printing. For example, it is more than highly recommended to |
|
never produce a source line that is wider than 78 columns. More |
|
on this below. |
|
|
|
|
|
1. Naming |
|
--------- |
|
|
|
a. Long and expressive labels |
|
|
|
Never hesitate to use long labels for your types, variables, |
|
etc.! Except maybe for things like very trivial types, the |
|
longest is the best, as it increases the source's |
|
_expressiveness_. Never forget that the role of a label is to |
|
express the `function' of the entity it represents, not its |
|
implementation! |
|
|
|
NOTE: Hungarian notation is NOT expressive, as it sticks the |
|
`type' of a variable to its name. A label like `usFoo' |
|
rarely tells the use of the variable it represents. |
|
|
|
And the state of a variable (global, static, dynamic) |
|
isn't helpful anymore. |
|
|
|
Conclusion: Avoid Hungarian Notation in FreeType 2. |
|
|
|
|
|
When forging a name with several nouns |
|
(e.g. `number-of-points'), use an uppercase letter for the first |
|
letter of each word (except the first), like: |
|
|
|
numberOfPoints |
|
|
|
You are also welcome to introduce underscores `_' in your |
|
labels, especially when sticking large nouns together, as it |
|
`airs' the code greatly. E.g.: |
|
|
|
`numberOfPoints' or `number_Of_Points' |
|
|
|
`IncredibleFunction' or `Incredible_Function' |
|
|
|
And finally, always put a capital letter after an underscore, |
|
except in variable labels that are all lowercase: |
|
|
|
`number_of_points' is OK for a variable (_all_ lowercase label) |
|
|
|
`incredible_function' is NOT for a function! |
|
^ ^ |
|
|
|
`Microsoft_windows' is a *shame*! |
|
^ ^ |
|
|
|
`Microsoft_Windows' isn't really better, but at least its a |
|
^ ^ correct function label within this |
|
convention ;-) |
|
|
|
b. Data types |
|
|
|
Try to use C types to the very least! Rely on internally |
|
defined equivalent types instead. For example, not all |
|
compilers agree on the sign of `char'; the size of `int' is |
|
platform-specific, etc. |
|
|
|
There are equivalents to the most common types in the |
|
`fttypes.h' public header file, like `FT_Short', `FT_UShort', |
|
etc. Using the internal types will guarantee that you won't |
|
need to replace every occurence of `short' or whatever when |
|
compiling on a weird platform or with a weird compiler, and |
|
there are many more than you could think of... |
|
|
|
c. Functions |
|
|
|
The name of a function should always begin with a capital |
|
letter, as lowercase first letters are reserved for variables. |
|
The name of a function should be, again, _expressive_! Never |
|
hesitate to put long function names in your code: It will make |
|
the code much more readable. |
|
|
|
Expressiveness doesn't necessarily imply lengthiness though; for |
|
instance, reading various data types from a file stream is |
|
performed using the following functions defined in the |
|
`ftstream.c' file of the `base' module: |
|
|
|
FT_Get_Char(), FT_Get_Short(), FT_Get_Long(), etc. |
|
|
|
Which is somewhat more readable than: |
|
|
|
cget, sget, usget, lget, etc. |
|
|
|
d. Variables |
|
|
|
Variable names (at least meant for the public interface) should |
|
always begin with a lowercase letter. Lowercase first letters |
|
are reserved for variables in this convention, as it has been |
|
already explained above. You are still welcome to use long and |
|
expressive variable names. |
|
|
|
Something like `numP' can express a number of pixels, porks, |
|
pancakes, and much more... Something like `num_points' won't. |
|
|
|
Unfortunately (mostly due to the lazyness of the developers), |
|
short variable names are still used in many parts of the |
|
library. Volunteers are highly welcome to improve this... |
|
|
|
As a side note, a field name of a structure counts as a variable |
|
name too. |
|
|
|
|
|
2. Declarations & Statements |
|
---------------------------- |
|
|
|
Try to align declarations and assignments in columns, if it proves |
|
logically. For example (taken from `ttraster.c'): |
|
|
|
struct TProfile_ |
|
{ |
|
FT_F26Dot6 X; /* current coordinate during sweep */ |
|
PProfile link; /* link to next profile - various purpose */ |
|
PLong offset; /* start of profile's data in render pool */ |
|
Int flow; /* profile orientation: asc/descending */ |
|
Long height; /* profile's height in scanlines */ |
|
Long start; /* profile's starting scanline */ |
|
|
|
UShort countL; /* number of lines to step before this */ |
|
/* profile becomes drawable */ |
|
|
|
PProfile next; /* next profile in same contour, used */ |
|
/* during drop-out control */ |
|
}; |
|
|
|
instead of |
|
|
|
struct TProfile_ |
|
{ |
|
FT_F26Dot6 X; /* current coordinate during sweep */ |
|
PProfile link; /* link to next profile - various purpose */ |
|
PLong offset; /* start of profile's data in render pool */ |
|
Int flow; /* profile orientation: asc/descending */ |
|
Long height; /* profile's height in scanlines */ |
|
Long start; /* profile's starting scanline */ |
|
UShort countL; /* number of lines to step before this */ |
|
/* profile becomes drawable */ |
|
PProfile next; /* next profile in same contour, used */ |
|
/* during drop-out control */ |
|
}; |
|
|
|
This comes from the fact that you are more interested in the field |
|
and its function than in its type. |
|
|
|
Or: |
|
|
|
x = i + 1; |
|
y += j; |
|
min = 100; |
|
|
|
instead of |
|
|
|
x=i+1; |
|
y+=j; |
|
min=100; |
|
|
|
And don't hesitate to separate blocks of declarations with |
|
newlines to `distinguish' logical sections. |
|
|
|
E.g., taken from an old source file, in the declarations of the |
|
CMap loader: |
|
|
|
long n, num_SH; |
|
unsigned short u; |
|
long off; |
|
unsigned short l; |
|
long num_Seg; |
|
unsigned short* glArray; |
|
long table_start; |
|
int limit, i; |
|
|
|
TCMapDir cmap_dir; |
|
TCMapDirEntry entry_; |
|
PCMapTable Plcmt; |
|
PCMap2SubHeader Plcmsub; |
|
PCMap4 Plcm4; |
|
PCMap4Segment segments; |
|
|
|
instead of |
|
|
|
long n, num_SH; |
|
unsigned short u; |
|
long off; |
|
unsigned short l; |
|
long num_Seg; |
|
unsigned short *glArray; |
|
long table_start; |
|
int limit, i; |
|
TCMapDir cmap_dir; |
|
TCMapDirEntry entry_; |
|
PCMapTable Plcmt; |
|
PCMap2SubHeader Plcmsub; |
|
PCMap4 Plcm4; |
|
PCMap4Segment segments; |
|
|
|
|
|
3. Blocks |
|
--------- |
|
|
|
Block separation is done with `{' and `}'. We do not use the K&R |
|
convention which becomes only useful with an extensive use of |
|
tabs. The `{' and its corresponding `}' should always be on the |
|
same column. It makes it easier to separate a block from the rest |
|
of the source, and it helps your _brain_ associate the accolades |
|
easily (ask any Lisp programmer on the topic!). |
|
|
|
Use two spaces for the next indentation level. |
|
|
|
Never use tabs in FreeType 2 code; their widths may vary with |
|
editors and systems. |
|
|
|
Example: |
|
|
|
if (condition_test) { |
|
waow mamma; |
|
I'm doing K&R format; |
|
just like the Linux kernel; |
|
} else { |
|
This test failed poorly; |
|
} |
|
|
|
should be rather formatted as |
|
|
|
if ( condition_test ) |
|
{ |
|
This code isn't stuck to the condition; |
|
read it on paper, you will find it more; |
|
pleasant to the eye; |
|
} |
|
else |
|
{ |
|
Of course, this is a matter of taste; |
|
This is just the way it is in this convention; |
|
and you should follow it to be homogenuous with; |
|
the rest of the FreeType code; |
|
} |
|
|
|
|
|
4. Macros |
|
--------- |
|
|
|
Macros should be made of uppercase letters. If a macro label is |
|
forged from several words, it is possible to only uppercasify the |
|
first word, using an underscore to separate the nouns. This is |
|
used in in some files for macros like |
|
|
|
GET_UShort(), USE_Stream(), etc. |
|
|
|
The role of macros used throughout the engine is explained later |
|
in this document. |
|
|
|
|
|
5. Conventions |
|
-------------- |
|
|
|
Currently, FreeType 2 source code uses the following formatting |
|
rules: |
|
|
|
. The data type is separated with two spaces from the variable, |
|
structure, or function name: |
|
|
|
const char foo; |
|
|
|
Usually, the `*' operator is concatenated to the data type: |
|
|
|
FT_Int* pointer; |
|
|
|
However, when declaring resp. defining an `output' parameter |
|
(i.e. a pointer which will be assigned by the function), the |
|
last `*' must be placed on the right in order to denote this, as |
|
in: |
|
|
|
FT_New_Face( FT_Library library, |
|
FT_Face *aface ); |
|
|
|
where the `*' is used to indicate that `aface' is returned. In |
|
most cases, the name of such an output variable starts with `a' |
|
or `an' (`aface' instead of `face', `anlru' instead of `lru', |
|
etc.), following the English rules of the indefinite article. |
|
|
|
. As mentioned above, multiple declarations are vertically |
|
aligned: |
|
|
|
FT_Short foo; |
|
FT_Long bar; |
|
FT_GlyphSlot slot; |
|
|
|
. Declarations are separated with two blank lines from the |
|
following code. This intentionally disturbs the code flow to |
|
make variable definitions more visible. |
|
|
|
{ |
|
char x, y; |
|
|
|
|
|
x = 3; |
|
y = 5; |
|
} |
|
|
|
. An opening parenthesis follows a function directly without |
|
space; after a built-in C keyword, one space is used: |
|
|
|
x = sin( y ); |
|
y = sizeof ( long ); |
|
|
|
Except for casts, empty parameters, and the closing semicolon, |
|
parentheses are surrounded with space: |
|
|
|
x = (char*)( foo + bar ); |
|
y = rand(); |
|
|
|
. Binary operators are surrounded by spaces; unary operators have |
|
no space after it: |
|
|
|
x = ( 3 + 4 ) / ( 7 - 2 ); |
|
y = -( 3 + 4 ) * 7; |
|
|
|
. Array arguments are not surrounded by spaces: |
|
|
|
array[3] = array[1] + array[2]; |
|
array[4] = array[1 + 3]; |
|
|
|
. Comma and semicolon have only space at the right side: |
|
|
|
if ( x = 0; x < y; x++, y-- ) |
|
do_something(); |
|
|
|
Exception: |
|
|
|
for (;;) |
|
{ |
|
... |
|
|
|
. Don't use |
|
|
|
if ( x == y ) a = b; |
|
|
|
but |
|
|
|
if ( x == y ) |
|
a = b; |
|
|
|
in general. |
|
|
|
. Preprocessor directives are never indented and always start in |
|
the first column. |
|
|
|
. All function/structure/variable definitions start at column |
|
three. |
|
|
|
. All full-line comments (except the header of a file) start at |
|
column three (even comments for preprocessor directives). |
|
|
|
. Labels are sticking out two positions to the left: |
|
|
|
switch ( x ) |
|
{ |
|
case 1: |
|
do_something(); |
|
break; |
|
default: |
|
do_nothing(); |
|
break; |
|
} |
|
|
|
|
|
|
|
II. Design Conventions |
|
====================== |
|
|
|
|
|
1. Modularity and Components Layout |
|
----------------------------------- |
|
|
|
The FreeType 2 engine has been designed with portability in mind. |
|
This implies the ability to compile and run it on a great variety |
|
of systems and weird environments, unlike many packages where the |
|
word strictly means `runs on a bunch of Unix-like systems'. We |
|
have thus decided to stick to the following restrictions: |
|
|
|
- The C version is written entirely in ANSI C. |
|
|
|
- The library, if compiled with gcc, doesn't produce any warning |
|
with the `-ansi -pedantic' flags. Other compilers with better |
|
checks may produce ANSI warnings -- please report. |
|
|
|
(NOTE: It can of course be compiled by an `average' C compiler, |
|
and even by a C++ one.) |
|
|
|
- It only requires in its simplest form an ANSI libc to compile, |
|
and no utilities other than a C preprocessor, compiler, and |
|
linker. |
|
|
|
- It consists of modules, starting with a `base' module which |
|
provides the API, some auxiliary modules used by the font |
|
drivers, the font driver modules itself, and the rasterizer |
|
modules. |
|
|
|
- The very low-level components can be easily replaced by |
|
system-specific ones that do not rely on the standard libc. |
|
These components deal mainly with i/o, memory, and mutex |
|
operations. |
|
|
|
- A client application only needs to include one header file named |
|
`freetype.h' to use the engine. Other public header files like |
|
`ftglyph.h' or `ftimage.h' provide functional extensions. |
|
|
|
- All configuration options are gathered in two files, |
|
`ftconfig.h' and `ftoption.h'. The former contains the |
|
processor and OS specific configuration options, while the |
|
latter treats options that may be enabled or disabled by the |
|
user to enable and disable various features. |
|
|
|
|
|
2. Configuration and Debugging |
|
------------------------------ |
|
|
|
Configuration is covered by the `BUILD' documentation file. |
|
|
|
Debugging is controlled by two macros in `ftoption.h', |
|
FT_DEBUG_LEVEL_ERROR and FT_DEBUG_LEVEL_TRACE; don't use them in |
|
code to be released. Check the source code of the `ftview.c' |
|
demonstration program (in the `ft2demos' package) how tracing can |
|
be used and activated. |
|
|
|
|
|
|
|
III. Usage conventions |
|
====================== |
|
|
|
|
|
1. Error Handling |
|
----------------- |
|
|
|
Most functions directly return an error code. A return value of 0 |
|
(FT_Err_Ok) means that no error occured, while a non-zero other |
|
value indicates a failure of any kind. |
|
|
|
We use code like this in FreeType 2: |
|
|
|
if ( ( rc = Perform_Action_1( parms_of_1 ) ) || |
|
( rc = Perform_Action_2( parms_of_2 ) ) || |
|
( rc = Perform_Action_3( parms_of_3 ) ) ) |
|
goto Fail; |
|
|
|
which is better but uses assignments within expressions, which are |
|
always delicate to manipulate in C (the risk of writing `==' |
|
exists, and would go unnoticed by a compiler). Moreover, the |
|
assignments are a bit redundant and don't express much things |
|
about the actions performed (they only speak of the error |
|
management issue). |
|
|
|
That is why some macros have been defined for the most frequently |
|
used functions. They relate to low-level routines that are called |
|
very often (mainly i/o and memory handling functions). Each macro |
|
produces an implicit assignment to a variable called `error' and |
|
can be used instead as a simple function call. Example: |
|
|
|
if ( PERFORM_Action_1( parms_of_1 ) || |
|
PERFORM_Action_2( parms_of_2 ) || |
|
PERFORM_Action_3( parms_of_3 ) ) |
|
goto Fail; |
|
|
|
with |
|
|
|
#define PERFORM_Action_1( parms_1 ) \ |
|
( error = Perform_Action_1( parms_1 ) ) |
|
#define PERFORM_Action_2( parms_1 ) \ |
|
( error = Perform_Action_2( parms_1 ) ) |
|
#define PERFORM_Action_3( parms_1 ) \ |
|
( error = Perform_Action_3( parms_1 ) ) |
|
|
|
defined in some header file. |
|
|
|
There, the developer only needs to define a local `error' variable |
|
and use the macros directly in the code, without caring about the |
|
actual error handling performed. Another advantage is that the |
|
structure of source files remain very similar, even though the |
|
error handling may be different. |
|
|
|
This convention is very close to the use of exceptions in |
|
languages like C++, Pascal, Java, etc. where the developer |
|
focuses on the actions to perform, and not on every little error |
|
checking. |
|
|
|
|
|
2. Font File I/O |
|
---------------- |
|
|
|
a. Streams |
|
|
|
The engine uses `streams' to access the font files. A stream is |
|
a structure containing information used to access files through |
|
a system-specific i/o library. |
|
|
|
The default implementation of streams uses the ANSI libc i/o |
|
functions. However, for the sake of embedding in light systems |
|
and independence of a complete C library, it is possible to |
|
re-implement the component for a specific system or OS, letting |
|
it use system calls. |
|
|
|
b. Frames |
|
|
|
TrueType is tied to the big-endian format, which implies that |
|
reading shorts or longs from the font file may need conversions |
|
depending on the target processor. To be able to easily detect |
|
read errors and allow simple conversion calls or macros, the |
|
engine is able to access a font file using `frames'. |
|
|
|
A frame is simply a sequence of successive bytes taken from the |
|
input file at the current position. A frame is pre-loaded into |
|
memory by a call to the `ACCESS_Frame()' macro. |
|
|
|
It is then possible to read all sizes of data through the |
|
`GET_xxx()' macros described above. |
|
|
|
When all important data is read, the frame can be released by a |
|
call to `FORGET_Frame()'. |
|
|
|
The benefits of frames are various. Consider these two |
|
approaches at extracting values: |
|
|
|
if ( ( error = Read_Short( &var1 ) ) || |
|
( error = Read_Long ( &var2 ) ) || |
|
( error = Read_Long ( &var3 ) ) || |
|
( error = Read_Short( &var4 ) ) ) |
|
|
|
return FAILURE; |
|
|
|
and |
|
|
|
/* Read the next 16 bytes */ |
|
if ( ACCESS_Frame( 16L ) ) |
|
return error; /* The Frame could not be read */ |
|
|
|
var1 = GET_Short(); /* extract values from the frame */ |
|
var2 = GET_Long(); |
|
var3 = GET_Long(); |
|
var4 = GET_Short(); |
|
|
|
FORGET_Frame(); /* release the frame */ |
|
|
|
In the first case, there are four error assignments with four |
|
checks of the file read. This unnecessarily increases the size |
|
of the generated code. Moreover, you must be sure that `var1' |
|
and `var4' are short variables, `var2' and `var3' long ones, if |
|
you want to avoid bugs and/or compiler warnings. |
|
|
|
In the second case, you perform only one check for the read, and |
|
exit immediately on failure. Then the values are extracted from |
|
the frame, as the result of function calls. This means that you |
|
can use automatic type conversion; there is no problem if |
|
e.g. `var1' and `var4' are longs, unlike previously. |
|
|
|
Finally, frames are ideal when you are using memory-mapped |
|
files, as the frame is not really `pre-loaded' and never uses |
|
any `heap' space. |
|
|
|
IMPORTANT: You CANNOT nest several frame accesses. There is |
|
only one frame available at a time for a specific |
|
instance. |
|
|
|
It is also the programmer's responsibility to never |
|
extract more data than was pre-loaded in the frame! |
|
(But you usually know how many values you want to |
|
extract from the file before doing so). |
|
|
|
|
|
3. Memory Management |
|
-------------------- |
|
|
|
The library now has a component which uses an interface similar to |
|
malloc()/free(). |
|
|
|
* FT_Alloc() |
|
|
|
To be used like malloc(), except that it returns an error code, |
|
not an address. Its arguments are the size of the requested |
|
block and the address of the target pointer to the `fresh' |
|
block. An error code is returned in case of failure (and this |
|
will also set the target pointer to NULL), 0 in case of success. |
|
|
|
FT_Alloc() internally calls the ft_alloc() function defined in |
|
an FT_Memory object. All error checking is done by FT_Alloc() |
|
itself so that ft_alloc() directly calls malloc(). |
|
|
|
* FT_Realloc() |
|
|
|
Similar to FT_Alloc(); it calls realloc() by default. |
|
|
|
* FT_Free() |
|
|
|
As you may have already guessed, FT_Free() is FT_Alloc()'s |
|
counterpart. It takes as argument the _target pointer's |
|
address_! You should _never_ pass the block's address directly, |
|
i.e. the pointer, to FT_Free(). |
|
|
|
Similar to FT_Alloc(), FT_Free() does the necessary error |
|
checking and calls free() by default. |
|
|
|
As the pointers addresses needed as arguments are typed `void**', |
|
ftmemory.h provides some macros to help use the above functions |
|
more easily, these are: |
|
|
|
MEM_Alloc() A version of FT_Alloc() that casts the argument |
|
pointer to (void**). Similar functions are |
|
MEM_Alloc_Array(), MEM_Realloc(), and |
|
MEM_Realloc_Array() |
|
|
|
ALLOC() Same as MEM_Alloc(), but with an assignment to a |
|
variable called `error'. See the section `error |
|
handling' above for more info on this. Similar |
|
functions are REALLOC(), ALLOC_ARRAY(), and |
|
REALLOC_ARRAY(). |
|
|
|
FREE() A version of FT_Free() that casts the argument |
|
pointer to (void**). |
|
|
|
MEM_Set() An alias for `memset()', which can be easily |
|
changed to anything else if you wish to use a |
|
different memory manager than the functions |
|
provided by the ANSI libc. |
|
|
|
MEM_Copy() An alias of `memcpy()' or `bcopy()' used to move |
|
blocks of memory. You may change it to something |
|
different if necessary (e.g. not using libc). |
|
|
|
MEM_Move() An alias of `memmove().' Change its definition if |
|
necessary. |
|
|
|
|
|
4. Support for threaded environments |
|
------------------------------------ |
|
|
|
Thread synchronisation has been dropped in FreeType 2. The |
|
library is already re-entrant, and if you really need two threads |
|
accessing the same FT_Library object, you should synchronize |
|
access to it yourself with a simple mutex. |
|
|
|
|
|
--- end of convntns.txt ---
|
|
|