📄 step2.html
字号:
described in a previous chapter. It is a enumeration that indicates
how the kerning distances are expressed in the target vector.</p>
<p>The default value is <tt>FT_KERNING_DEFAULT</tt> which has
value 0. It corresponds to kerning distances expressed in 26.6
grid-fitted pixels (which means that the values are multiples of 64).
For scalable formats, this means that the design kerning distance is
scaled, then rounded.</p>
<p>The value <tt>FT_KERNING_UNFITTED</tt> corresponds to kerning
distances expressed in 26.6 unfitted pixels (i.e., that do not
correspond to integer coordinates). It is the design kerning distance
that is scaled without rounding.</p>
<p>Finally, the value <tt>FT_KERNING_UNSCALED</tt> is used to return
the design kerning distance, expressed in font units. You can later
scale it to the device space using the computations explained in the
last chapter of this section.</p>
<p>Note that the ‘left’ and ‘right’ positions
correspond to the <em>visual order</em> of the glyphs in the string of
text. This is important for bidirectional text, or simply when
writing right-to-left text.</p>
<hr>
<h3>
4. Simple text rendering: kerning + centering
</h3>
<p>In order to show off what we just learned, we will now demonstrate
how to modify the example code that was provided in section I to
render a string of text, and enhance it to support kerning and delayed
rendering.</p>
<h4>
a. Kerning support
</h4>
<p>Adding support for kerning to our code is trivial, as long as we
consider that we are still dealing with a left-to-right script like
Latin. We simply need to retrieve the kerning distance between two
glyphs in order to alter the pen position appropriately. The code
looks like:</p>
<div class="pre">
FT_GlyphSlot slot = face->glyph; <span class="comment">/* a small shortcut */</span>
FT_UInt glyph_index;
FT_Bool use_kerning;
FT_UInt previous;
int pen_x, pen_y, n;
... initialize library ...
... create face object ...
... set character size ...
pen_x = 300;
pen_y = 200;
use_kerning = FT_HAS_KERNING( face );
previous = 0;
for ( n = 0; n < num_chars; n++ )
{
<span class="comment">/* convert character code to glyph index */</span>
glyph_index = FT_Get_Char_Index( face, text[n] );
<span class="comment">/* retrieve kerning distance and move pen position */</span>
if ( use_kerning && previous && glyph_index )
{
FT_Vector delta;
FT_Get_Kerning( face, previous, glyph_index,
ft_kerning_mode_default, &delta );
pen_x += delta.x >> 6;
}
<span class="comment">/* load glyph image into the slot (erase previous one) */</span>
error = FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER );
if ( error )
continue; <span class="comment">/* ignore errors */</span>
<span class="comment">/* now draw to our target surface */</span>
my_draw_bitmap( &slot->bitmap,
pen_x + slot->bitmap_left,
pen_y - slot->bitmap_top );
<span class="comment">/* increment pen position */</span>
pen_x += slot->advance.x >> 6;
<span class="comment">/* record current glyph index */</span>
previous = glyph_index;
}
</div>
<p>We are done. Some notes:</p>
<ul>
<li>
<p>As kerning is determined from glyph indices, we need to
explicitely convert our character codes into a glyph indices, then
later call <tt>FT_Load_Glyph</tt> instead of
<tt>FT_Load_Char</tt>.</p>
</li>
<li>
<p>We use a boolean named <tt>use_kerning</tt> which is set with
the result of the macro <tt>FT_HAS_KERNING</tt>. It is certainly
faster not to call <tt>FT_Get_Kerning</tt> when we know that the
font face does not contain kerning information.</p>
</li>
<li>
<p>We move the position of the pen <em>before</em> a new glyph is
drawn.</p>
</li>
<li>
<p>We initialize the variable <tt>previous</tt> with the
value 0, which always corresponds to the ‘missing
glyph’ (also called <tt>.notdef</tt> in the Postscript
world). There is never any kerning distance associated with this
glyph.</p>
</li>
<li>
<p>We do not check the error code returned by
<tt>FT_Get_Kerning</tt>. This is because the function always sets
the content of <tt>delta</tt> to (0,0) when an error occurs.</p>
</li>
</ul>
<h4>
b. Centering
</h4>
<p>Our code begins to become interesting but it is still a bit too
simple for normal use. For example, the position of the pen is
determined before we do the rendering; normally, you would rather
layout the text and measure it before computing its final position
(centering, etc.) or perform things like word-wrapping.</p>
<p>Let us now decompose our text rendering function into two distinct
but successive parts: The first one will position individual glyph
images on the baseline, while the second one will render the glyphs.
As we will see, this has many advantages.</p>
<p>We will thus start by storing individual glyph images, as well as
their position on the baseline. This can be done with code like:</p>
<div class="pre">
FT_GlyphSlot slot = face->glyph; <span class="comment">/* a small shortcut */</span>
FT_UInt glyph_index;
FT_Bool use_kerning;
FT_UInt previous;
int pen_x, pen_y, n;
FT_Glyph glyphs[MAX_GLYPHS]; <span class="comment">/* glyph image */</span>
FT_Vector pos [MAX_GLYPHS]; <span class="comment">/* glyph position */</span>
FT_UInt num_glyphs;
... initialize library ...
... create face object ...
... set character size ...
pen_x = 0; <span class="comment">/* start at (0,0) */</span>
pen_y = 0;
num_glyphs = 0;
use_kerning = FT_HAS_KERNING( face );
previous = 0;
for ( n = 0; n < num_chars; n++ )
{
<span class="comment">/* convert character code to glyph index */</span>
glyph_index = FT_Get_Char_Index( face, text[n] );
<span class="comment">/* retrieve kerning distance and move pen position */</span>
if ( use_kerning && previous && glyph_index )
{
FT_Vector delta;
FT_Get_Kerning( face, previous, glyph_index,
FT_KERNING_DEFAULT, &delta );
pen_x += delta.x >> 6;
}
<span class="comment">/* store current pen position */</span>
pos[num_glyphs].x = pen_x;
pos[num_glyphs].y = pen_y;
<span class="comment">/* load glyph image into the slot without rendering */</span>
error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
if ( error )
continue; <span class="comment">/* ignore errors, jump to next glyph */</span>
<span class="comment">/* extract glyph image and store it in our table */</span>
error = FT_Get_Glyph( face->glyph, &glyphs[num_glyphs] );
if ( error )
continue; <span class="comment">/* ignore errors, jump to next glyph */</span>
<span class="comment">/* increment pen position */</span>
pen_x += slot->advance.x >> 6;
<span class="comment">/* record current glyph index */</span>
previous = glyph_index;
<span class="comment">/* increment number of glyphs */</span>
num_glyphs++;
}
</div>
<p>This is a very slight variation of our previous code where we
extract each glyph image from the slot, and store it, along with the
corresponding position, in our tables.</p>
<p>Note also that <tt>pen_x</tt> contains the total advance for the
string of text. We can now compute the bounding box of the text
string with a simple function like:</p>
<div class="pre">
void compute_string_bbox( FT_BBox *abbox )
{
FT_BBox bbox;
<span class="comment">/* initialize string bbox to "empty" values */</span>
bbox.xMin = bbox.yMin = 32000;
bbox.xMax = bbox.yMax = -32000;
<span class="comment">/* for each glyph image, compute its bounding box, */</span>
<span class="comment">/* translate it, and grow the string bbox */</span>
for ( n = 0; n < num_glyphs; n++ )
{
FT_BBox glyph_bbox;
FT_Glyph_Get_CBox( glyphs[n], ft_glyph_bbox_pixels,
&glyph_bbox );
glyph_bbox.xMin += pos[n].x;
glyph_bbox.xMax += pos[n].x;
glyph_bbox.yMin += pos[n].y;
glyph_bbox.yMax += pos[n].y;
if ( glyph_bbox.xMin < bbox.xMin )
bbox.xMin = glyph_bbox.xMin;
if ( glyph_bbox.yMin < bbox.yMin )
bbox.yMin = glyph_bbox.yMin;
if ( glyph_bbox.xMax > bbox.xMax )
bbox.xMax = glyph_bbox.xMax;
if ( glyph_bbox.yMax > bbox.yMax )
bbox.yMax = glyph_bbox.yMax;
}
<span class="comment">/* check that we really grew the string bbox */</span>
if ( bbox.xMin > bbox.xMax )
{
bbox.xMin = 0;
bbox.yMin = 0;
bbox.xMax = 0;
bbox.yMax = 0;
}
<span class="comment">/* return string bbox */</span>
*abbox = bbox;
}
</div>
<p>The resulting bounding box dimensions are expressed in integer
pixels and can then be used to compute the final pen position before
rendering the string as in:</p>
<div class="pre">
<span class="comment">/* compute string dimensions in integer pixels */</span>
string_width = string_bbox.xMax - string_bbox.xMin;
string_height = string_bbox.yMax - string_bbox.yMin;
<span class="comment">/* compute start pen position in 26.6 cartesian pixels */</span>
start_x = ( ( my_target_width - string_width ) / 2 ) * 64;
start_y = ( ( my_target_height - string_height ) / 2 ) * 64;
for ( n = 0; n < num_glyphs; n++ )
{
FT_Glyph image;
FT_Vector pen;
image = glyphs[n];
pen.x = start_x + pos[n].x;
pen.y = start_y + pos[n].y;
error = FT_Glyph_To_Bitmap( &image, FT_RENDER_MODE_NORMAL,
&pen, 0 );
if ( !error )
{
FT_BitmapGlyph bit = (FT_BitmapGlyph)image;
my_draw_bitmap( bit->bitmap,
bit->left,
my_target_height - bit->top );
FT_Done_Glyph( image );
}
}
</div>
<p>Some remarks:</p>
<ul>
<li>
<p>The pen position is expressed in the cartesian space (i.e.,
y upwards).</p>
</li>
<li>
<p>We call <tt>FT_Glyph_To_Bitmap</tt> with the <tt>destroy</tt>
parameter set to 0 (false), in order to avoid destroying the
original glyph image. The new glyph bitmap is accessed through
<tt>image</tt> after the call and is typecasted to
<tt>FT_BitmapGlyph</tt>.</p>
</li>
<li>
<p>We use translation when calling <tt>FT_Glyph_To_Bitmap</tt>.
This ensures that the <tt>left</tt> and <tt>top</tt> fields of the
bitmap glyph object are already set to the correct pixel
coordinates in the cartesian space.</p>
</li>
<li>
<p>Of course, we still need to convert pixel coordinates from
cartesian to device space before rendering, hence the
<tt>my_target_height - bitmap->top</tt> in the call to
<tt>my_draw_bitmap</tt>.</p>
</li>
</ul>
<p>The same loop can be used to render the string anywhere on our
display surface, without the need to reload our glyph images each
time. We could also decide to implement word wrapping, and only
draw</p>
<hr>
<h3>
5. Advanced text rendering: transformation + centering + kerning
</h3>
<p>We are now going to modify our code in order to be able to easily
transform the rendered string, for example to rotate it. We will start
by performing a few minor improvements:</p>
<h4>
a. packing & translating glyphs
</h4>
<p>We start by packing the information related to a single glyph image
into a single structure instead of parallel arrays. We thus define
the following structure type:</p>
<div class="pre">
typedef struct TGlyph_
{
FT_UInt index; <span class="comment">/* glyph index */</span>
FT_Vector pos; <span class="comment">/* glyph origin on the baseline */</span>
FT_Glyph image; <span class="comment">/* glyph image */</span>
} TGlyph, *PGlyph;
</div>
<p>We also translate each glyph image directly after it is loaded to
its position on the baseline at load time. As we will see, this as
several advantages. Our glyph sequence loader thus becomes:</p>
<div class="pre">
FT_GlyphSlot slot = face->glyph; <span class="comment">/* a small shortcut */</span>
FT_UInt glyph_index;
FT_Bool use_kerning;
FT_UInt previous;
int pen_x, pen_y, n;
TGlyph glyphs[MAX_GLYPHS]; <span class="comment">/* glyphs table */</span>
PGlyph glyph; <span class="comment">/* current glyph in table */</span>
FT_UInt num_glyphs;
... initialize library ...
... create face object ...
... set character size ...
pen_x = 0; <span class="comment">/* start at (0,0) */</span>
pen_y = 0;
num_glyphs = 0;
use_kerning = FT_HAS_KERNING( face );
previous = 0;
glyph = glyphs;
for ( n = 0; n < num_chars; n++ )
{
glyph->index = FT_Get_Char_Index( face, text[n] );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -