It is targetted to FreeType hackers, or more simply to developers who would like a better understanding of the library's source code.
But an example is certainly more meaningful than anything else. The following code:
error = read_short(stream, &str.value1);
if (error) goto ...
error = read_ulong(stream, &str.value2);
if (error) goto ...
error = read_ulong(stream, &str.value3);
if (error) goto ...
can easily be replaced with:
error = FT_Access_Frame(stream, 2+4+4);
if (error) goto ...
str.value1 = FT_Get_Short(stream);
str.value2 = FT_Get_ULong(stream);
str.value3 = FT_Get_ULong(stream);
FT_Forget_Frame(stream);
Here, the call to FT_Access_Frame will:
Each FT_Get_Short or FT_Get_ULong call will read a big-endian integer from the stream (2 bytes for FT_Get_Short, 4 bytes for FT_Get_ULong) and advance the frame cursor accordingly.
FT_Forget_Frame "releases" the frame from memory
There are several advantages to using frames :
Note that error must be a local variable of type FT_Error,
while stream must be a local variable or argument of type FT_Stream;
The macro used to access a frame is ACCESS_Frame(_size_), it will translate to:
Similarly, the macro FORGET_Frame() translates to:
Extracting integers can be performed with the GET_xxx macros, like:
Macro name | Translation | Description |
GET_Byte() | (FT_Get_Byte(stream)) | reads an 8-bit unsigned byte |
GET_Char() | ((FT_Char)FT_Get_Byte(stream)) | reads an 8-bit signed byte |
GET_Short() | (FT_Get_Short(stream)) | reads a 16-bit signed big-endian integer |
GET_UShort() | ((FT_UShort)FT_Get_Short(stream)) | reads a 16-bit unsigned big-endian integer |
GET_Offset() | (FT_Get_Offset(stream)) | reads a 24-bit signed big-endian integer |
GET_UOffset() | ((FT_UOffset)FT_Get_Offset(stream)) | reads a 24-bit unsigned big-endian integer |
GET_Long() | (FT_Get_Long(stream)) | reads a 32-bit signed big-endian integer |
GET_ULong() | ((FT_ULong)FT_Get_Long(stream)) | reads a 32-bit unsigned big-endian integer |
(Note that an Offset is an integer stored with 3 bytes on the file).
All this means that the following code:
error = FT_Access_Frame(stream, 2+4+4);
if (error) goto ...
str.value1 = FT_Get_Short(stream);
str.value2 = FT_Get_ULong(stream);
str.value3 = FT_Get_ULong(stream);
FT_Forget_Frame(stream);
Can be replaced with macros by:
if ( ACCESS_Frame( 2+4+4 ) ) goto ...
str.value1 = GET_Short();
str.value2 = GET_ULong();
str.value3 = GET_ULong();
FORGET_Frame();
Which is clearer. Notice that error and stream must be defined locally though for this code to work.. !!
For example, FT_Read_Short( stream, &error ) reads and returns a 2-byte big-endian integer from a stream, and place an error code in the error variable.
Thus, reading a single big-endian integer is shorter than using a frame for it.
Note that there is also the macros READ_xxx() which translate to:
( FT_Read_xxx(stream,&error), error != FT_Err_Ok )
and can be used as in:
if ( READ_UShort(variable1) || READ_ULong (variable2) ) goto Fail;
when error and stream are already defined locally..