Friday, October 9, 2015

Character Bitmap display - 3. use Freetype lib.

2.3 Use Freetype libraury

  • 可變大小字體
之前在LCD上顯示的字體為固定大小,無法調整大小,這邊利用 Freetype libra. 來讓字體可以旋轉、改變大小。


  • 原理簡述
Freetype 將一個文字,分解成關鍵點、貝塞爾曲線,利用文字關鍵點、透過貝塞爾曲線連接關鍵點,形成若干個閉合曲線所構成的文字,再填充文字內部,即可形成一個向量圖型文字,好處是只需要儲存少量資訊,且顯示字體的大小,不受解析度影響,既放大字體不會降低繪製的精度。


  • 文字顯示過程
  1. 給定一個文字,可以確定文字的編碼值,例如 GBK, Unicode, BIG5, ...等
  2. 根據"文字的編碼值"可以從字體文件中,找到對應的glyph
  3. 設置字體大小、旋轉角度
  4. 用內建函數把glyph裡的向量圖型縮放為設置字體大小、與角度
  5. 轉換為位圖點陣
  6. 在LCD上顯示出來

  • 官方文件摘要
1. Initialize the library

Simply create a variable of type FT_Library named, for example, library, and call the function FT_Init_FreeType as in
#include <ft2build.h> #include FT_FREETYPE_H FT_Library library; ... error = FT_Init_FreeType( &library ); if ( error ) { ... an error occurred during library initialization ... }

2. Load a font face
a. From a font file
Create a new face object by calling FT_New_Face. A face describes a given typeface and style. For example, ‘Times New Roman Regular’ and ‘Times New Roman Italic’ correspond to two different faces.
FT_Library library; /* handle to library */ FT_Face face; /* handle to face object */ error = FT_Init_FreeType( &library ); if ( error ) { ... } error = FT_New_Face( library, "/usr/share/fonts/truetype/arial.ttf", 0, &face ); if ( error == FT_Err_Unknown_File_Format ) { ... the font file could be opened and read, but it appears ... that its font format is unsupported } else if ( error ) { ... another error code means that the font file could not ... be opened or read, or simply that it is broken... }
Its parameters are

library
    A handle to the FreeType library instance where the face object is created.

filepathname
    The font file pathname (a standard C string).

face_index
    Certain font formats allow several font faces to be embedded in a single file.
    This index tells which face you want to load. An error will be returned if its value is too large.
    Index 0 always work though.

face
    A pointer to the handle that will be set to describe the new face object.
    It is set to NULL in case of error.

b. From memory
FT_Library library; /* handle to library */ FT_Face face; /* handle to face object */ error = FT_Init_FreeType( &library ); if ( error ) { ... } error = FT_New_Memory_Face( library, buffer, /* first byte in memory */ size, /* size in bytes */ 0, /* face_index */ &face ); if ( error ) { ... }

c. From other sources (compressed files, network, etc.)


4. Accessing face content


5. Setting the current pixel size
FreeType 2 uses size objects to model all information related to a given character size for a given face. For example, a size object will hold the value of certain metrics like the ascender or text height, expressed in 1/64th of a pixel, for a character size of 12 points.
When the FT_New_Face function is called (or one of its cousins), it automatically creates a new size object for the returned face. This size object is directly accessible as face−>size.
NOTE: A single face object can deal with one or more size objects at a time; however, this is something that few programmers really need to do. We have thus decided to simplify the API for the most common use (i.e., one size per face) while keeping this feature available through additional functions.
When a new face object is created, all elements are set to 0 during initialization. To populate the structure with sensible values, simply call FT_Set_Char_Size. Here is an example where the character size is set to 16pt for a 300×300dpi device:
error = FT_Set_Char_Size( face, /* handle to face object */ 0, /* char_width in 1/64th of points */ 16*64, /* char_height in 1/64th of points */ 300, /* horizontal device resolution */ 300 ); /* vertical device resolution */
Some notes:
  • The character widths and heights are specified in 1/64th of points. A point is a physical distance, equaling 1/72th of an inch. Normally, it is not equivalent to a pixel.
  • A value of 0 for the character width means ‘same as character height’, a value of 0 for the character height means ‘same as character width’. Otherwise, it is possible to specify different character widths and heights.
  • The horizontal and vertical device resolutions are expressed in dots-per-inch, or dpi. Normal values are 72 or 96 dpi for display devices like the screen. The resolution is used to compute the character pixel size from the character point size.
  • A value of 0 for the horizontal resolution means ‘same as vertical resolution’, a value of 0 for the vertical resolution means ‘same as horizontal resolution’. If both values are zero, 72 dpi is used for both dimensions.
  • The first argument is a handle to a face object, not a size object.
This function computes the character pixel size that corresponds to the character width and height and device resolutions. However, if you want to specify the pixel sizes yourself, you can simply call FT_Set_Pixel_Sizes, as in
error = FT_Set_Pixel_Sizes( face, /* handle to face object */ 0, /* pixel_width */ 16 ); /* pixel_height */
This example will set the character pixel sizes to 16×16 pixels. As previously, a value of 0 for one of the dimensions means ‘same as the other’.
Note that both functions return an error code. Usually, an error occurs with a fixed-size font format (like FNT or PCF) when trying to set the pixel size to a value that is not listed in the face->fixed_sizes array.

6. Loading a glyph image
a. Converting a character code into a glyph index
We will describe later how to look for specific charmaps in a face. For now, we will assume that the face contains at least a Unicode charmap that was selected during a call to FT_New_Face. To convert a Unicode character code to a font glyph index, we use FT_Get_Char_Index, as in
glyph_index = FT_Get_Char_Index( face, charcode );
This will look up the glyph index corresponding to the given charcode in the charmap that is currently selected for the face. You should use the UTF-32 representation form of Unicode; for example, if you want to load character U+1F028, use value 0x1F028 as the value for charcode.
If no charmap was selected, the function simply returns the charcode.

b. Loading a glyph from the face
Loading a glyph image into the slot is performed by calling FT_Load_Glyph as in
error = FT_Load_Glyph( face, /* handle to face object */ glyph_index, /* glyph index */ load_flags ); /* load flags, see below */
The load_flags value is a set of bit flags used to indicate some special operations. The default value FT_LOAD_DEFAULTis 0.

The field face−>glyph−>format describes the format used to store the glyph image in the slot. If it is notFT_GLYPH_FORMAT_BITMAP, one can immediately convert it to a bitmap through FT_Render_Glyph as in:
error = FT_Render_Glyph( face->glyph, /* glyph slot */ render_mode ); /* render mode */
The parameter render_mode is a set of bit flags used to specify how to render the glyph image. Set it toFT_RENDER_MODE_NORMAL to render a high-quality anti-aliased (256 gray levels) bitmap, as this is the default. You can alternatively use FT_RENDER_MODE_MONO if you want to generate a 1-bit monochrome bitmap.

c. Using other charmaps
There are two ways to select a different charmap with FreeType 2. The easiest is when the encoding you need already has a corresponding enumeration defined in FT_FREETYPE_H, for example FT_ENCODING_BIG5. In this case, you can simply call FT_Select_CharMap as in:
error = FT_Select_CharMap( face, /* target face object */ FT_ENCODING_BIG5 ); /* encoding */
Another way is to manually parse the list of charmaps for the face; this is accessible through the fieldsnum_charmaps and charmaps (notice the ‘s&rsquo) of the face object. As you could expect, the first is the number of charmaps in the face, while the second is a table of pointers to the charmaps embedded in the face.

d. Glyph transformations
It is possible to specify an affine transformation to be applied to glyph images when they are loaded. Of course, this will only work for scalable (vectorial) font formats.
To do that, simply call FT_Set_Transform, as in:
error = FT_Set_Transform( face, /* target face object */ &matrix, /* pointer to 2x2 matrix */ &delta ); /* pointer to 2d vector */
This function will set the current transform for a given face object. Its second parameter is a pointer to a simpleFT_Matrix structure that describes a 2×2 affine matrix. The third parameter is a pointer to a FT_Vector structure that describes a simple two-dimensional vector that is used to translate the glyph image after the 2×2 transformation.

  • 具體代碼流程
  1. initialize library: FT_Init_FreeType( &library )
  2. create face object: FT_New_Face( library, argv[1], 0, &face )
  3. Setting the current pixel size: FT_Set_Char_Size  or FT_Set_Pixel_Sizes 
  4. set transformation: FT_Set_Transform( face, &matrix, &pen )
  5. load glyph image into the slot: FT_Load_Char( face, chinese_str[n], FT_LOAD_RENDER ) 
  6. draw to our target surface: draw_bitmap( &slot->bitmap, slot->bitmap_left, target_height - slot->bitmap_top )
上述第5步驟 load glyph image into the slot,可分解如下四步驟
a:FT_Select_CharMap( face, FT_ENCODING_BIG5 );
選擇編碼方式
b:FT_Get_Char_Index( face, charcode )
把字元編碼轉換為索引值,根據該索引就可以找到glyph
c:FT_Load_Glyph( face,glyph_index, load_flags );
從face中取出glyph
d:FT_Render_Glyph( face->glyph, render_mode );
將glyph轉換為點陣圖


實作:在 ubuntu 12.04 平台的 console 列印文字
==================================
example1.c

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <wchar.h>

#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H


#define WIDTH   80
#define HEIGHT  80


/* origin is the upper left corner */
unsigned char image[HEIGHT][WIDTH];


/* Replace this function with something useful. */

void
draw_bitmap( FT_Bitmap*  bitmap,
             FT_Int      x,
             FT_Int      y)
{
  FT_Int  i, j, p, q;
  FT_Int  x_max = x + bitmap->width;
  FT_Int  y_max = y + bitmap->rows;


  for ( i = x, p = 0; i < x_max; i++, p++ )
  {
    for ( j = y, q = 0; j < y_max; j++, q++ )
    {
      if ( i < 0      || j < 0       ||
           i >= WIDTH || j >= HEIGHT )
        continue;

      image[j][i] |= bitmap->buffer[q * bitmap->width + p];
    }
  }
}


void
show_image( void )
{
  int  i, j;


  for ( i = 0; i < HEIGHT; i++ )
  {
  printf("%02d", i);
    for ( j = 0; j < WIDTH; j++ )
      putchar( image[i][j] == 0 ? ' '
                                : image[i][j] < 128 ? '+'
                                                    : '*' );
    putchar( '\n' );
  }
}


int
main( int     argc,
      char**  argv )
{
  FT_Library    library;
  FT_Face       face;

  FT_GlyphSlot  slot;
  FT_Matrix     matrix;                 /* transformation matrix */
  FT_Vector     pen;                    /* untransformed origin  */
  FT_Error      error;

  char*         filename;
  char*         text;

  double        angle;
  int           target_height;
  int           n, num_chars;
  
  //
  // int chinese_str[] = {0x97e6, 0x4e1c, 0x5c71, 0x0067};
  // char *str = "韋東山";
  wchar_t *chinese_str = L"韋gif";      // wchar_t 單位四個byte
  unsigned int *p = (wchar_t *)chinese_str;
  int i;
  FT_BBox bbox;
  FT_Glyph glyph;
  
  // print char unicode
  printf("Unicode: \n");
  for(i = 0; i < wcslen(chinese_str); i++)
  {
  printf("0x%x ", p[i]);
  }
  printf("\n"); 

  if ( argc != 2 )
  {
    fprintf ( stderr, "usage: %s font\n", argv[0] );
    exit( 1 );
  }

  filename      = argv[1];                           /* first argument     */
  // text          = argv[2];                        /* second argument    */
  // num_chars     = strlen( text );
  angle         = ( 0.0 / 360 ) * 3.14159 * 2;      /* use 25 degrees     */
  target_height = HEIGHT;

  error = FT_Init_FreeType( &library );              /* initialize library */
  /* error handling omitted */

  error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */
  /* error handling omitted */

  #if 0
  /* use 50pt at 100dpi */
  // pixels = (50 * 64 /64 /72) * 100 = 69
error = FT_Set_Char_Size( face, 50 * 64, 0,
                            100, 0 );                /* set character size */
  #else
FT_Set_Pixel_Sizes(face, 24, 0);
  #endif
  /* error handling omitted */

  slot = face->glyph;

  /* set up matrix */
  matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
  matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
  matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
  matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );

  /* the pen position in 26.6 cartesian space coordinates; */
  /* start at (0,40) relative to the upper left corner  */
  pen.x = 0 * 64;
  pen.y = ( target_height - 40 ) * 64;

  for ( n = 0; n < wcslen(chinese_str); n++ )
  {
    /* set transformation */
    FT_Set_Transform( face, &matrix, &pen );

    /* load glyph image into the slot (erase previous one) */
    error = FT_Load_Char( face, chinese_str[n], FT_LOAD_RENDER );
    if ( error )
      continue;                 /* ignore errors */
    error = FT_Get_Glyph(face -> glyph, &glyph);
    if(error)
    {
        printf("FT_Get_Glyph ERROR !\n");
        return -1;
    }

    FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox);


    /* now, draw to our target surface (convert position) */
    draw_bitmap( &slot->bitmap,
                 slot->bitmap_left,
                 target_height - slot->bitmap_top );
printf("Unicode: 0x%x\n", chinese_str[n]);
printf("origin.x/64 = %d, origin.y/64 = %d\n", pen.x/64, pen.y/64);
printf("xMin = %d, xMax = %d, yMin = %d, yMax = %d\n", bbox.xMin, bbox.xMax, bbox.yMin, bbox.yMax);
    printf("slot -> advance.x/64 = %d, slot -> advance.y/64 = %d\n", slot -> advance.x/64, slot -> advance.y/64);
    

    /* increment pen position */
    pen.x += slot->advance.x;
    pen.y += slot->advance.y;
  }

  show_image();

  FT_Done_Face    ( face );
  FT_Done_FreeType( library );

  return 0;
}

/* EOF */
========================================================
測試
  1. tar freetype-2.4.10
  2. ./configure
  3. make
  4. sudo make install
  5. 4-1. libra. 安裝到  /usr/local/lib
  6. 4-2. 標頭檔安裝到 /usr/local/include/freetype2/freetype/
  7. gcc -finput-charset=GBK -fexec-charset=UTF-8 -o example1 example1.c  -I /usr/local/include/freetype2 -lfreetype -lm
-I: 指定標頭檔目錄
-lfreetype: 指定所使用的libra.
-lm: 指定數學函數庫

執行
 ./example1 simsun.ttc
20          +++                                                                 
21          +**                                                                 
22          +*+      ++                                                         
23          +*+     +**+                                                        
24 +**++++++***+++++***++                  **          +****+                   
25          +*+                            **         +**++**                   
26          +*+     +                                 **+  **                   
27          +*+    +*+                                **+                       
28   +**++++***++++**++                               **                        
29          +*+              +****++**  *****      *********                    
30          +*+       +     +**++**++*    +**         **                        
31          +*+      +*+    **+  +**       **         **                        
32+**+++++++***++++++***    **+  +**       **         **                        
33          +*+      **+    **+  +**       **         **                        
34          +*+      **     +**++**+       **         **                        
35          +*+     +*+     +*****+        **         **                        
36          +*+     +*+     **+            **         **                        
37          +*+ ++++**+     *****+++       **         **                        
38          +*+ ++****     ++*******+     +**+       +**+                       
39          +*+   +**+     **+    +**   ********    *******                     
40          +*+    +       **+    +**                                           
41          +*+            ***++++**+                                           
42          ++              +******+                                            



參考資料:
  1. 嵌入式Linux應用開發完全手冊
  2. 韋東山視頻三期
  3. https://zh.wikipedia.org/wiki/FreeType (wiki FreeType介紹)
  4. https://zh.wikipedia.org/wiki/%E4%BD%8D%E5%9B%BE (Bitmap介紹)
  5. https://zh.wikipedia.org/wiki/%E5%AD%97%E4%BD%93%E5%85%89%E6%A0%85%E5%8C%96 (字行光柵化介紹)
  6. https://zh.wikipedia.org/wiki/%E5%8F%AF%E7%B8%AE%E6%94%BE%E5%90%91%E9%87%8F%E5%9C%96%E5%BD%A2 (可縮放向量圖形介紹)
  7. https://zh.wikipedia.org/wiki/%E7%9F%A2%E9%87%8F%E5%9B%BE%E5%BD%A2 (向量圖形介紹)
  8. https://zh.wikipedia.org/wiki/%E6%A0%85%E6%A0%BC%E5%8C%96 (柵格化)

2 comments: