📄 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 + -