freetype矢量字体 —— 例程1源码解析

例程源码exmaple1

本地源码路径freetype-2.10.2/docs/tutorial/example1.c
or 官网地址:https://freetype.org/freetype2/docs/tutorial/example1.c

例程主要工作:在控制台打印一行字符串对应的用freetype处理后用于显示的bitmap,第一个字符左上角坐标(300, 200),模拟的LCD区域大小WIDTH x HEIGHT = 640 x 480。

其中,image[][]数组用于存放模拟lcd的像素值,相当于lcd的Framebuffer。

注意:如果要在LCD上显示文字内容, 就将draw_bitmap中的image[j][i]赋值语句, 替换为写LCD的Framebuffer缓冲区,而且无需调用show_image。

每行代码解析,见下方代码注释:

/* example1.c                                                      */
/*                                                                 */
/* This small program shows how to print a rotated string with the */
/* FreeType 2 library.                                             */

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

// 1. 包含freetype头文件
#include <ft2build.h>
#include FT_FREETYPE_H // for freetype 基础API

// 用于显示的目标LCD区域大小
#define WIDTH   640
#define HEIGHT  480

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

/* Replace this function with something useful. */

// 如果要在LCD上显示文字内容, 就将image[j][i]赋值语句, 替换为写LCD的Framebuffer缓冲区

/**
  以LCD坐标(x, y)为起始位置, 显示bitmap
  @param bitmap 要绘制的bitmap, 通常包含在slot中
  @param x 要绘制的bitmap的起始横坐标
  @param y 要绘制的bitmap的起始纵坐标
  @details 坐标采用的LCD坐标系
*/
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 simplicity, we assume that `bitmap->pixel_mode' */
  /* is `FT_PIXEL_MODE_GRAY' (i.e., not a bitmap font)   */

  for ( i = x, p = 0; i < x_max; i++, p++ ) // x 轴方向
  {
    for ( j = y, q = 0; j < y_max; j++, q++ ) // y轴方向
    {
      if ( i < 0      || j < 0       ||
           i >= WIDTH || j >= HEIGHT )
        continue;
    
      // 将bitmap中1写入image矩阵
      // LCD的y轴对应image的行数, x轴对应列数
      image[j][i] |= bitmap->buffer[q * bitmap->width + p];
    }
  }
}

/**
  串口打印image内容, 即一行字符串对应的bitmap内容
 */
void
show_image( void )
{
  int  i, j;

  for ( i = 0; i < HEIGHT; 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; // freetype库
  FT_Face       face;    // face对象, 包含了字体信息

  FT_GlyphSlot  slot;  // 插槽slot, 保存了处理后的bitmap
  FT_Matrix     matrix;                 /* transformation matrix */
  FT_Vector     pen;                    /* untransformed origin  */
  FT_Error      error; // 错误码

  char*         filename; // 字体文件
  char*         text;     // 要显示的一行字符串

  double        angle;   // 要旋转的角度
  int           target_height; // 用于显示的目标区域高度, 通常为LCD高度
  int           n, num_chars;

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

  // 字体文件名
  filename      = argv[1];                           /* first argument     */
  // 要显示的文本内容
  text          = argv[2];                           /* second argument    */ 
  // 要显示的文本长度
  num_chars     = strlen( text ); 
  // 旋转角度, 单位为弧度
  angle         = ( 25.0 / 360 ) * 3.14159 * 2;      /* use 25 degrees     */
  target_height = HEIGHT;

  // 2. 初始化freetype库
  error = FT_Init_FreeType( &library );              /* initialize library */
  /* error handling omitted */

  // 3. 加载/打开字体face, 会创建一个face对象
  error = FT_New_Face( library, filename, 0, &face );/* create face object */
  /* error handling omitted */

  // 4. 设置字体大小, 单位pt
  /* use 50pt at 100dpi */
  error = FT_Set_Char_Size( face, 50 * 64, 0,
                            100, 0 );                /* set character size */
  /* error handling omitted */

  // 5. (略)选择charmap: FT_Select_Charmap

  /* cmap selection omitted;                                        */
  /* for simplicity we assume that the font contains a Unicode cmap */

  slot = face->glyph;  //  获取glyph slot, 存放了glyph image

  // 设置旋转矩阵
  /* 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 );

  /* 反推出第一个字符的origin */
  // 设置pen, 即绘制字体的起始位置, 笛卡尔坐标
  // 要显示的第一个字符左上角LCD坐标(300,200), 转换为笛卡尔坐标(300, target_height - 200)
  // *64是因为FT_Set_Transform要求坐标单位是1/64像素
  /* the pen position in 26.6 cartesian space coordinates; */
  /* start at (300,200) relative to the upper left corner  */
  pen.x = 300 * 64;
  pen.y = ( target_height - 200 ) * 64;

  // 循环显示字符串的所有字符
  // 按行显示
  for ( n = 0; n < num_chars; n++ )
  {
    // 9. 移动/旋转glyph image
    /* set transformation */
    FT_Set_Transform( face, &matrix, &pen );

    // 5/6/7. 加载glyph image到slot, 可以直接得到bitmap, 存放在slot中
    /* load glyph image into the slot (erase previous one) */
    error = FT_Load_Char( face, text[n], FT_LOAD_RENDER );
    if ( error )
      continue;                 /* ignore errors */

    // 绘制每个字符的bitmap
    // 字符外框左上角坐标(bitmap_left, bitmap_top), 转换为LCD坐标(bitmap_left, target_height - bitmap_top)
    /* now, draw to our target surface (convert position) */
    draw_bitmap( &slot->bitmap,
                 slot->bitmap_left,
                 target_height - slot->bitmap_top );

    // 计算下一个字体的起始位置origin
    /* increment pen position */
    pen.x += slot->advance.x;
    pen.y += slot->advance.y;
  }

  show_image(); // 控制台打印bitmap对应的image数组, 如果要在LCD显示, 就应该写framebuffer

  FT_Done_Face    ( face ); // 释放指定face对象及其子slots和尺寸, 与FT_New_Face配对
  FT_Done_FreeType( library ); // 释放字体文件缓存, 与FT_Init_FreeType配对

  return 0;
}

/* EOF */

注:例程中,第一个字符的左上角位置是直接给定的,并没有按整行字符串的外框来调整。


小结

freetype显示字体流程

使用freetype绘制一行字符通用流程:
1)包含freetype头文件;
2)初始化freetype库:FT_Init_FreeType;
3)加载字体文件,创建face对象:FT_New_Face;
4)设置字体大小:FT_Set_Char_Size(单位pt)或FT_Set_Pixel_Size(单位pixel);
5)(可选)设置charmap(非默认编码时选用): FT_Select_Charmap;
6)根据编码值charcode寻找glyph_index: FT_Get_Char_Index;
7)根据glyph_index取出glyph: FT_Load_Glyph;
8)将glyph image转换为bitmap: FT_Render_Glyph
9)移动或旋转字符origin: FT_Set_Transform,坐标单位要求1/64像素;
10)显示bitmap:调用自定义draw_bitmap

步骤6,7,8 可以用一个函数替代FT_Load_Char(face, charcode, FT_LOAD_RENDER),直接得到glyph对应bitmap。

参考下图描述:

截取自 https://freetype.org/freetype2/docs/tutorial/index.html

LCD与笛卡尔坐标系转换

LCD显示使用LCD坐标系,freetype库使用笛卡尔坐标系。注意不同坐标系下的坐标转换。
已知LCD区域,通常是LCD屏幕大小(H, V)(宽、高)。那么,LCD坐标系下的(x, y),对应笛卡尔坐标系下(x, V - y)

posted @ 2022-09-04 17:29  明明1109  阅读(1103)  评论(0编辑  收藏  举报