1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970
|
Conventions and Design in the FreeType library
----------------------------------------------
Table of Contents
Introduction
I. Style and Formatting
1. Naming
2. Declarations & Statements
3. Blocks
4. Macros
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 (due to change soon)
4. Support for threaded environments
5. Object Management
Introduction
============
This text introduces the many conventions used within the FreeType
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. Components
A unit of the library is called a `component'. Each component
has at least an interface, and often a body. The library comes
in two language flavors, C and Pascal (the latter severely out
of date unfortunately). A component in C is defined by two
files, one `.h' header and one `.c' body, while a Pascal
component is contained in a single `.pas' file.
All component source file names begin with the `tt' prefix, with
the exception of the `FreeType' component. For example, the
file component is implemented by the files `ttfile.h',
`ttfile.c', and `ttfile.pas'. Only lowercase letters should be
used, following the 8+3 naming convention to allow compilation
under DOS.
In the C version, a single component can have multiple bodies.
For example, `ttfile.c' provides stream i/o through standard
ANSI libc calls, while `ttfile2.c' implements the same thing
using a Unix memory-mapping API.
The FreeType component is an interface-only component.
b. 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.
Avoid Hungarian Notation like the *plague*!
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 welcomed 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 ;-)
c. Types
All types that are defined for use by FreeType client
applications are defined in the FreeType component. All types
defined there have a label beginning with `TT_'. Examples:
TT_Face, TT_F26Dot6, etc.
However, the library uses a lot more of internal types that are
defined in the Types, Tables, and Objs components (`tttypes' &
`tttables' files).
By convention, all internal types, except the simplest ones like
integers, have their name beginning with a capital `T', like in
'TFoo'. Note that the first letter of `foo' is also
capitalized. The corresponding pointer type uses a capital `P'
instead, i.e. (TFoo*) is simply named 'PFoo'. Examples:
typedef struct _TTableDir
{
TT_Fixed version; /* should be 0x10000 */
UShort numTables; /* Tables number */
UShort searchRange; /* These parameters are only used */
UShort entrySelector;/* for a dichotomy search in the */
UShort rangeShift; /* directory. We ignore them. */
} TTableDir;
typedef TTableDir* PTableDir;
Note that we _always_ define a typedef for structures. The
original struct label starts with `_T'.
This convention is a famous one from the Pascal world.
Try to use C or Pascal 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 `Types'
components, like `Short', `UShort', etc. Using the internal
types will guarantee that you won't need to replace every
occurence of `short' or wathever when compiling on a weird
platform or with a weird compiler, and there are many more than
you could think of...
d. 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 shorts from a file stream is performed using
the following functions defined in the `File' component:
Get_Byte, Get_Short, Get_UShort, Get_Long, etc.
Which is somewhat more readable than:
cget, sget, usget, lget, etc.
e. Variables
Variable names 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're
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.
Today, we are still using short variable labels in some parts of
the library. We're working on removing them however...
As a side note, a field name of a structure counts as a variable
name too. There are exceptions to the first-lowercase-letter
rule, but these are only related to fields within the structure
defined by the TrueType specification (well, at least it
_should_ be that way).
2. Declarations & Statements
----------------------------
a. Columning
Try to align declarations and assignments in columns, if it
proves logical. For example (taken from `ttraster.c'):
struct _TProfile
{
Int flow; /* Profile orientation : Asc/Descending */
Int height; /* profile's height in scanlines */
Int start; /* profile's start scanline */
ULong offset; /* offset of profile's data in render pool */
PProfile link; /* link to next profile */
Int index; /* index of profile's entry in trace table */
Int count_lines; /* count of lines having to be drawn */
Int start_line; /* lines to be rendered before this profile */
PTraceRec trace; /* pointer to profile's current trace table */
};
instead of
struct _TProfile {
Int flow; /* Profile orientation : Asc/Descending */
Int height; /* profile's height in scanlines */
Int start; /* profile's start scanline */
ULong offset; /* offset of profile's data in render pool */
PProfile link; /* link to next profile */
Int index; /* index of profile's entry in trace table */
Int count_lines; /* count of lines having to be drawn */
Int start_line; /* lines to be rendered before this profile */
PTraceRec trace; /* pointer to profile's current trace table */
};
This comes from the fact that you're more interested by the
field and its function than by 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;
b. Aliases and the `with' clause
The Pascal language comes with a very handy `with' clause that
is often used when dealing with the fields of a same record.
The following Pascal source extract
with table[incredibly_long_index] do
begin
x := some_x;
y := some_y;
z := wathever_the_hell;
end;
is usually translated to:
table[incredibly_long_index].x = some_x;
table[incredibly_long_index].y = some_y;
table[incredibly_long_index].z = wathever_the_hell;
When a lot of fields are involved, it is usually helpful to
define an `alias' for the record, like in:
alias = table + incredibly_long_index;
alias->x = some_x;
alias->y = some_y;
alias->z = wathever_the_hell;
which gives cleaner source code, and eases the compiler's
optimization work.
Though the use of aliases is currently not fixed in the current
library source, it is useful to follow one of these rules:
- Avoid an alias with a stupid, or cryptic name, something like:
TFooRecord tfr;
....
[lots of lines snipped]
....
tfr = weird_table + weird_index;
...
tfr->num = n;
It doesn't really help to guess what 'tfr' stands for several
lines after its declaration, even if it's an extreme
contraction of one particular type.
Something like `cur_record' or `alias_cmap' is better. The
current source also uses a prefix of `Pl' for such aliases
(like Pointer to Local alias), but this use is _not_
encouraged. If you want to use prefixes, use `loc_', `cur_',
or `al_' at the very least, with a descriptive name following.
- Or simply use a local variable with a semi-expressive name:
{
THorizontalHeader hheader;
TVerticalHeader vheader;
hheader = instance->fontRes->horizontalHeader;
vheader = instance->fontRes->verticalHeader;
hheader->foo = bar;
vheader->foo = bar2;
...
}
which is much better than
{
THorizontalHeader Plhhead;
TVerticalHeader Plvhead;
Plhhead = instance->fontRes->horizontalHeader;
Plvhead = instance->fontRes->verticalHeader;
Plhhead->foo = bar;
Plvhead->foo = bar2;
...
}
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_ associates the accolades
easily (ask any Lisp programmer on the topic!).
Use two spaces for the next indentation level.
Never use tabs in your 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;
}
is _OUT_!
if ( condition_test )
{
This code isn't stuck to the condition;
read it on paper, you'll find it more;
pleasant to the eye;
}
else
{
Of course, this is a matter of taste;
That's just the way it is in this convention;
and you should follow it to be homogenous with;
the rest of the FreeType code;
}
is _IN_!
4. Macros
---------
Macros should be made of uppercase letters. When 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 ttload.c, ttgload.c and ttfile.c with macros like
ACCESS_Frame, GET_UShort, CUR_Stream
The role of the macros used throughout the engine is explained
later in this document.
II. Design Conventions
======================
1. Modularity and Components Layout
-----------------------------------
The FreeType 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 in ANSI C. The Pascal version compiles
and run under Turbo Pascal 5.0 and compatible compilers.
- 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 that we'd be happy to now
about.
(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 pre-processor, compiler, and
linker.
- It is written in a modular fashion. Each module is called a
`component' and is made of two files in the C version (an
interface with suffix `.h' and body with suffix `.c' ) and one
file in the Pascal one.
- 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 must only include one interface file named
`freetype.h' resp. `freetype.pas' to use the engine. All other
components should never be used or accessed by client
applications, and their name always begin with a `tt' prefix:
ttmemory, ttobjs, ttinterp, ttapi, etc.
- All configuration options are gathered in two files. One
contains the processor and OS specific configuration options,
while the other treats options that may be enabled or disabled
by the developer to test specific features (like assertions,
debugging, etc).
IMPORTANT NOTES:
These restrictions only apply to the core engine. The package
that comes with it contains several test programs sources that
are much less portable, even if they present a modular model
inspired from the engine's layout.
The components currently found in the `lib' directory are:
-------- high-level interface ----------------------------------
freetype.h High-level API, to be used by client applications.
ttapi.c Implementation of the api found in `freetype.h'.
-------- configuration -----------------------------------------
ttconfig.h Engine configuration options. These are commented
and switched by hand by the developer. See
section 2 below for more info.
ft-conf.h Included by ttconfig.h, this file isn't part of
the `lib' directory, but depends on the target
environment. See section 2 blow for more info.
-------- definitions -------------------------------------------
tttypes.h The engine's internal types definitions.
tttables.h The TrueType tables definitions, per se the Specs.
tttags.h The TrueType table tags definitions.
tterror.[ch] The error and debugging component.
ttdebug.[ch] Only used by the debugger, should not be linked
into a release build.
ttcalc.[ch] Math component used to perform some computations
with an intermediate 64-bit precision.
-------- replaceable components --------------------------------
ttmemory.[ch] Memory component. This version uses the ANSI libc
but can be replaced easily by your own version.
ttfile.[ch] Stream i/o component. This version uses the ANSI
libc but can be replaced easily by your own
version. Compiled only if file memory mapping
isn't available on your system.
ttfile2.[ch] Unix-specific file memory mapping version of the
file component. It won't compile on other
systems. Usually results in much faster file
access (about 2x on a SCSI P166 system)
ttmutex.[ch] Generic mutex component. This version is a dummy
and should only be used for a single-thread build.
You _need_ to replace this component's body with
your own implementation to be able to build a
threaded version of the engine.
-------- data management ---------------------------------------
ttengine.h The engine instance record definition, root of all
engine data.
ttlists.[ch] Generic lists manager.
ttcache.[ch] Generic cache manager.
ttobjs.[ch] The engine's object definitions and
implementations module contains structures,
constructors, destructors and methods for the
following objects:
face, instance, glyph, execution_context
ttload.[c] The TrueType tables loader.
ttgload.[ch] The glyph loader. A component in itself, due to
the task's complexity.
ttindex.[ch] The character mapping to glyph index conversion
routines. Implements functions defined in
`freetype.h'.
ttinterp.[ch] The TrueType instructions interpreter. Probably
the nicest source in this engine. Apparently,
many have failed to produce a comparable one due
to the very poorly written specification! It took
David Turner three months of his spare time to get
it working correctly! :-)
ttraster.[ch] The engine's second best piece. This is the
scan-line converter. Performs gray-level
rendering (also known as font-smoothing) as well
as dropout-control.
2. Configuration and Debugging
------------------------------
As stated above, configuration depends on two files:
The environment configuration file `ft-conf.h':
This file contains the definitions of many configuration options
that are processor and OS-dependent. On Unix systems, this file
is generated automatically by the `configure' script that comes
with the released package.
On other environments, it is located in one of the architecture
directories found in `arch' (e.g. `arch/os2/ft-conf.h').
The path to this file should be passed to the compiler when
compiling _each_ component. (typically with an -I option).
The engine configuration file `ttconfig.h':
This file contains many configuration options that the developer
can turn on or off to experiment with some `features' of the
engine that are not part of its `simplest' form. The options
are commented.
Note that the makefiles are compiler-specific.
It is possible to enable the dumping of debugging information by
compiling the components with the various debug macros. Please
consult the file `ttconfig.h' for details.
If you want to port the engine to another environment, you will
need to
- Write a new `ft-conf.h' file for it. Just copy one of those
available and change the flags accordingly (they're all
commented).
- Replace the memory, file, and mutex components with yours,
presenting the same interface and behaviour.
- Eventually add some code in ttapi.c to initialize
system-specific data with the engine.
III. Usage conventions
======================
1. Error Handling
-----------------
Error handling has been refined to allow reentrant builds of the
library, available only in the C version. We thus have now two
different conventions.
In Pascal:
A global error variable is used to report errors when they are
detected. All functions return a boolean that indicates success
or failure of the call. If an error occurs within a given
function, the latter must set the error variable and return
`false' (which means failure).
It is then possible to make several calls in a single `if'
statement like:
if not Perform_Action_1( parms_of_1 ) or
not Perform_Action_2( parms_of_2 ) or
not Perform_Action_3( parms_of_3 ) then goto Fail;
where execution will jump to the `Fail' label whenever an error
occurs in the sequence of actions invoked in the condition.
In C:
Global errors are forbidden in re-entrant builds. Each function
thus returns directly an error code. A return value of 0 means
that no error occured, while a non-zero other value indicates a
failure of any kind.
This convention is more constraining than the one used in the
Pascal source. The above Pascal statement should be translated
into the following C fragment:
rc = Perform_Action_1( parms_of_1 );
if ( rc )
goto Fail;
rc = Perform_Action_2( parms_of_2 );
if ( rc )
goto Fail;
rc = Perform_Action_3( parms_of_3 );
if ( rc )
goto Fail;
which, while being equivalent, isn't as pleasantly readable.
One `simple' way to match the original fragment would be to
write:
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, mutex, 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 at the beginning of the 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. Examples of such a
usage can be found in `ttload.c' and `ttgload.c'. Moreover, the
structure of the source files remain very similar, even though
the error handling is very 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 defined in the `File' component containing
information used to access files through a system-specific i/o
library.
The current implementation of the File component uses the ANSI
libc i/o functions. However, for the sake of embedding in light
systems and independence of a complete libc, it is possible to
re-implement the component for a specific system or OS, letting
it use system calls.
A stream is of type `TStream' defined in the `TTObjs' interface.
The type is `(void*)' but actually points to a structure defined
within the File component.
A stream is created, managed and closed through the interface of
the `File' component. Several implementations of the same
component can co-exist, each taking advantage of specific system
features (the file `ttfile2.c' uses memory-mapped files for
instance) as long as it respects the interface.
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 `TT_Access_Frame()' call of the `File' component.
It is then possible to read all sizes of data through the
`Get_xxx()' functions, like Get_Byte(), Get_Short(),
Get_UShort(), etc.
When all important data is read, the frame can be released by a
call to `TT_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 ( (error = TT_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();
TT_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.
On big-endian machines, the `Get_xxx()' functions could also be
simple macros that merely peek the values directly from the
frame, which speeds up and simplifies the generated code!
And 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 uses a component which interface is similar to
malloc()/free(). It defines only two functions.
* 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.
Alloc() should always respect the following rules:
- Requesting a block of size 0 should set the target pointer to
NULL and return no error code (i.e., return 0).
- The returned block is always zeroed. This is an important
assumption of other parts of the library.
If you wish to replace the memory component with your own,
please respect this behaviour, or your engine won't work
correctly.
* Free()
As you may have already guessed, Free() is 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 Free().
Free should always respect the following rules:
- Calling it with a NULL argument, or the address of a NULL
pointer is valid, and should return success.
- The pointer is always set to NULL after the block's
deallocation. This is also an important assumption of many
other parts of the library.
If you wish to replace the memory component with your own,
please respect this behaviour, or your engine won't work
correctly.
As the pointers addresses needed as arguments are typed `void**',
the component's interface also provides in the C version some
macros to help use them more easily, these are:
MEM_Alloc A version of Alloc that casts the argument pointer
to (void**).
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.
FREE A version of Free() that casts the argument
pointer to (void**). There is currently no error
handling by with this macro.
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 you wish to use something else that
your standard libc.
4. Support for threaded environments
------------------------------------
Support for threaded environments have been added to the C
sources, and only to these. It is now theorically possible to
build three distinct versions of the library:
single-thread build:
The default build. This one doesn't known about different
threads. Hence, no code is generated to perform coherent data
sharing and locking.
thread-safe build:
With this build, several threads can use the library at the
same time. However, some key components can only be used by
one single thread at a time, and use a mutex to synchronize
access to their functions. These are mainly the file, raster
and interpreter components.
re-entrant build:
A re-entrant version is able to perform certain actions in
parallel that a thread-safe one cannot. This includes
accessing file(s) in parallel, interpreting different
instruction streams in parallel, or even scan-line converting
distinct glyphs at the same time.
Note that most of the latest changes in the engine are making the
distinction between the thread-safe and re-entrant builds thinner
than ever.
There is a `ttmutex' component that presents a generic interface
to mutex operations. It should be re-implemented for each
platform.
<to be continued>
--- end of convntns.txt ---
|