freetype用法

news2024/9/28 21:19:51

freetype用法


`

文章目录

  • freetype用法
  • 0.实现
    • 1.变量定义
    • 2.lcd操作获取屏幕信息
    • 3.freetype初始化
    • 4.绘画
  • 1.字形度量
  • 2.类
    • 1.FT 中的面向对象
    • 2.FT_Library 类
    • 3.FT_Face 类
    • 4 FT_Size 类
    • 5 FT_GlyphSlot 类
  • 3.函数
    • 1.把一个字符码转换为一个字形索引FT_Get_Char_Index函数
    • 2.从 face 中装载一个字形FT_Load_Glyph与FT_Render_Glyph函数
    • 3.提取字形图像FT_Get_Glyph函数
    • 4.变换和复制字形图像FT_Glyph_Transform函数
    • 5.测量字形图像FT_Glyph_Get_CBox函数
    • 6.转换字形图像为位图FT_Glyph_To_Bitmap函数
  • 实例代码


0.实现

/* color : 0x00RRGGBB */
void lcd_put_pixel(int x, int y, unsigned int color)
{
    // 获取当前像素的地址
    unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width;
    unsigned short *pen_16; 
    unsigned int *pen_32;   

    unsigned int red, green, blue;  

    pen_16 = (unsigned short *)pen_8;
    pen_32 = (unsigned int *)pen_8;

    switch (var.bits_per_pixel)
    {
        case 8:
        {
            // 8位色
            *pen_8 = color;
            break;
        }
        case 16:
        {
            // 16位色
            red   = (color >> 16) & 0xff;
            green = (color >> 8) & 0xff;
            blue  = (color >> 0) & 0xff;
            color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
            *pen_16 = color;
            break;
        }
        case 32:
        {
            // 32位色
            *pen_32 = color;
            break;
        }
        default:
        {
            printf("can't surport %dbpp\n", var.bits_per_pixel);
            break;
        }
    }
}

lcd_put_pixel 函数接受三个参数:x,y 和 color。它用于在指定的 x 和 y 坐标上设置屏幕上像素的颜色。color 参数是一个无符号整数,表示像素的颜色,格式为 0x00RRGGBB,其中 RR 表示红色分量,GG 表示绿色分量,BB 表示蓝色分量

/* 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;

    //printf("x = %d, y = %d\n", x, y);

  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 >= var.xres || j >= var.yres )
        continue;

      //image[j][i] |= bitmap->buffer[q * bitmap->width + p];
      lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]);
    }
  }
}

draw_bitmap函数用于在屏幕上绘制一个位图。它接受三个参数:bitmap是要绘制的位图,x和y是位图的左上角坐标。该函数使用lcd_put_pixel函数将位图的像素写入屏幕。

int Get_Glyphs_Frm_Wstr(FT_Face face, wchar_t * wstr, TGlyph glyphs[])
{
    int n;
    PGlyph glyph = glyphs;
    int pen_x = 0;
    int pen_y = 0;
    int error;
    FT_GlyphSlot  slot = face->glyph;;
    
        
    for (n = 0; n < wcslen(wstr); n++)
    {
        glyph->index = FT_Get_Char_Index( face, wstr[n]); 
        /* store current pen position */ 
        glyph->pos.x = pen_x; 
        glyph->pos.y = pen_y;       

        /* load时是把glyph放入插槽face->glyph */
        error = FT_Load_Glyph(face, glyph->index, FT_LOAD_DEFAULT);
        if ( error ) 
            continue;

        error = FT_Get_Glyph(face->glyph, &glyph->image ); 
        if ( error ) 
            continue;

        /* translate the glyph image now */ 
        /* 这使得glyph->image里含有位置信息 */
        FT_Glyph_Transform(glyph->image, 0, &glyph->pos );

        pen_x += slot->advance.x;  /* 1/64 point */

        /* increment number of glyphs */ 
        glyph++;        
    }

    /* count number of glyphs loaded */ 
    return (glyph - glyphs);
}

compute_string_bbox函数接受一个TGlyph数组glyphs,数组中的字形数量num_glyphs和一个FT_BBox指针abbox。它通过循环遍历glyphs数组中的每个字形并使用FT_Glyph_Get_CBox检索其边界框来计算字符串的边界框。然后,该函数通过将字形边界框与当前整体边界框进行比较来更新整体边界框。

1.变量定义

   FT_Library    library; // FreeType 库
    FT_Face       face; // 字体
    int error; // 错误码
    FT_Vector     pen; // 绘制笔
    FT_GlyphSlot  slot; // 字形插槽
    int i; // 循环计数器
    FT_BBox bbox; // 字符串边界框

    int line_box_ymin = 10000; // 行边界框最小 y 坐标
    int line_box_ymax = 0; // 行边界框最大 y 坐标

    int line_box_width; // 行边界框宽度
    int line_box_height; // 行边界框高度

    TGlyph glyphs[MAX_GLYPHS]; /* 字形表 */ 
    FT_UInt num_glyphs; // 字形数量

2.lcd操作获取屏幕信息

   fd_fb = open("/dev/fb0", O_RDWR);
    if (fd_fb < 0)
    {
        printf("can't open /dev/fb0\n");
        return -1;
    }


    // 获取屏幕信息
    if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
    {
        printf("can't get var\n");
        return -1;
    }

    // 获取固定屏幕信息
    if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix))
    {
        printf("can't get fix\n");
        return -1;
        }
    // 获取屏幕宽度
    line_width  = var.xres * var.bits_per_pixel / 8;
    // 获取像素宽度
    pixel_width = var.bits_per_pixel / 8;
    // 获取屏幕大小
    screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
    // 映射屏幕内存
    fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
    if (fbmem == (unsigned char *)-1)
    {
        printf("can't mmap\n");
        return -1;
    }

3.freetype初始化

/* 全部设为黑色 */
memset(fbmem, 0, screen_size);

/* 初始化FreeType库 /
error = FT_Init_FreeType( &library ); /
initialize library /
/
错误处理省略 */

/* 创建字体对象 /
error = FT_New_Face( library, argv[1], 0, &face ); /
create face object /
/
错误处理省略 */
slot = face->glyph;

/* 设置字体大小 */
FT_Set_Pixel_Sizes(face, 24, 0);

4.绘画

/* wstr1 */
// 将字符串转换为字形表
num_glyphs = Get_Glyphs_Frm_Wstr(face, wstr1, glyphs);

// 计算字符串的边界框
compute_string_bbox(glyphs, num_glyphs, &bbox);
line_box_width  = bbox.xMax - bbox.xMin;
line_box_height = bbox.yMax - bbox.yMin;

// 计算字符串在屏幕上的位置
pen.x = (var.xres - line_box_width)/2 * 64;
pen.y = (var.yres - line_box_height)/2 * 64;

// 绘制字符串
Draw_Glyphs(glyphs, num_glyphs, pen);


/* wstr2 */
num_glyphs = Get_Glyphs_Frm_Wstr(face, wstr2, glyphs);

compute_string_bbox(glyphs, num_glyphs, &bbox);
line_box_width  = bbox.xMax - bbox.xMin;
line_box_height = bbox.yMax - bbox.yMin;

pen.x = (var.xres - line_box_width)/2 * 64;
pen.y = pen.y - 24 * 64;
Draw_Glyphs(glyphs, num_glyphs, pen);

1.字形度量

从字形度量这个名字可以想到,字形度量是关联每一个字形的确定距离,以此描述如何使用该距离来排版文本。
通常一个字形有两个度量集:用来排版水平文本排列的字形(拉丁文、西里尔文、阿拉伯文、希伯来文等等)和用来排版垂直文本排列的字形(中文、日文、韩文等等)。
要注意的是只有很少的字体格式提供了垂直度量。你可以使用宏 FT_HAS_VERTICAL 测试某个给出的face 对象是否包含垂直度量,当结果为真时表示包含。
个别的字形度量可以先装载字形到 face 的字形槽,然后通过 face->glyph->metrics 结构访问,其类型为FT_Glyph_Metrics。我们将在下面详细讨论它,现在,我们只关注该结构包含如下的字段:
Width
这是字形图像的边框的宽度。它与排列方向无关。
Height
这是字形图像的边框的高度。它与排列方向无关。
horiBearingX
用于水平文本排列,这是从当前光标位置到字形图像最左边的边界的水平距离。
horiBearingY
用于水平文本排列,这是从当前光标位置(位于基线)到字形图像最上边的边界的水平距离。
horiAdvance
用于水平文本排列,当字形作为字符串的一部分被绘制时,这用来增加笔位置的水平距离。
vertBearingX
用于垂直文本排列,这是从当前光标位置到字形图像最左边的边框的垂直距离。
vertBearingY
用于垂直文本排列,这是从当前光标位置(位于基线)到字形图像最上边的边框的垂直距离。
vertAdvance
用于垂直文本排列,当字形作为字符串的一部分被绘制时,这用来增加笔位置的垂直距离。
注意:因为不是所有的字体都包含垂直度量,当 FT_HAS_VERTICAL 为假时,vertBearingX,vertBearingY和 vertAdvance 的值是不可靠的。
下面的图形更清楚地图解了度量。第一个图解了水平度量,其基线为水平轴:
在这里插入图片描述

对于垂直文本排列,基线是垂直的,与垂直轴一致:
在这里插入图片描述

对于垂直文本排列,基线是垂直的,与垂直轴一致:
Face->glyph->metrics 中的度量通常以 26.6 象素格式(例如 1/64 象素)表示,除非你在调用 FT_Load_Glyph
或 FT_Load_Char 时使用了 FT_LOAD_NO_SCALE 标志,这样的话度量会用原始字体单位表示。
字形槽(glyph slot)对象也有一些其他有趣的字段可以减轻开发者的工作。你可以通过 face->glyph->xxx 访问它们,其中 xxx 是下面字段之一:
Advance
这个字段是一个 FT_Vector,保存字形的变换推进。当你通过 FT_Set_Transform 使用变换时,这是很有用的,这在第一部分的循环文本例子中已经展示过了。与那不同,这个值是默认的(metrics.horiAdvance,0),除非你在装载字形图像时指定 FT_LOAD_VERTICAL,那么它将会为(0,metrics.vertAdvance)。
linearHoriAdvance
这个字段包含字形水平推进宽度的线性刻度值。实际上,字形槽返回的 metrics.horiAdvance 值通常四舍五入为整数象素坐标(例如,它是 64 的倍数),字体驱动器用它装载字形图像。linearHoriAdvance 是一个 16.16固定浮点数,提供了以 1/65536 象素为单位的原始字形推进宽度的值。它可以用来完成伪设备无关文字排版。
linearVertAdvance
这与 linearHoriAdvance 类似,但它用于字形的垂直推进高度。只有当字体 face 包含垂直度量时这个值才是可靠的。

2.类

1.FT 中的面向对象

虽然 FT 是使用 ANSI C 编写,但是采用面向对象的思想,是这个库非常容易扩展,因此,下面有一些代码规约。

  1. 每个对象类型/类都有一个对应的结构类型和一个对应的结构指针类型,后者称为类型/类的句柄类型
    设想我们需要管理 FT 中一个 foo 类的对象,可以定义如下
typedef struct FT_FooRec_* FT_Foo;
typedef struct FT_FooRec_
{
// fields for the foo class}FT_FooRec;

依照规约,句柄类型使用简单而有含义的标识符,并以 FT_开始,如 FT_Foo,而结构体使用相同的名称但是加上 Rec 后缀。 Rec 是记录的缩写。每个类类型都有对应的句柄类型;
2. 类继承通过将基类包装到一个新类中实现,例如,我们定义一个 foobar 类,从 foo 类继承,可以实现为

typedef struct FT_FooBarRec_ * FT_FooBar;
typedef struct FT_FooBarRec_
{
FT_FooRec root; //基类
}FT_FooBarRec;

可以看到,将一个 FT_FooRec 放在 FT_FooBarRec 定义的开始,并约定名为 root,可以确保一个 foobar 对象也是一个 foo 对象。
在实际使用中,可以进行类型转换。

2.FT_Library 类

这个类型对应一个库的单一实例句柄,没有定义相应的 FT_LibraryRec,使客户应用无法访问它的内部属性。 库对象是所有 FT 其他对象的父亲,你需要在做任何事情前创建一个新的库实例,销毁它时会自动销毁他所有的孩子,如 face 和 module 等。 通常客户程序应该调用 FT_Init_FreeType()来创建新的库对象,准备作其他操作时使用。 另一个方式是通过调用函数 FT_New_Library()来创建一个新的库对象,它在<freetype/ftmodule.h> 中 定 义 , 这 个 函 数 返 回 一 个 空 的 库 , 没 有 任 何 模 块 注 册 , 你 可 以 通 过 调 用FT_Add_Module()来安装模块。
调用 FT_Init_FreeType()更方便一些,因为他会缺省地注册一些模块。这个方式中,模块列表在构建时动态计算,并依赖 ftinit 部件的内容。

3.FT_Face 类

一个外观对象对应单个字体外观,即一个特定风格的特定外观类型,例如 Arial 和 Arial Italic 是两个不同的外观。
一个外观对象通常使用 FT_New_Face()来创建,这个函数接受如下参数:一个 FT_Library 句柄,一个表示字体文件的 C 文件路径名,一个决定从文件中装载外观的索引(一个文件中可能有不同的外观),和 FT_Face句柄的地址,它返回一个错误码。

FT_Error FT_New_Face( FT_Library library,
const char* filepathname,
FT_Long face_index,
FT_Face* face);

函数调用成功,返回 0, face 参数将被设置成一个非 NULL 值。外观对象包含一些用来描述全局字体数据的属性,可以被客户程序直接访问。例如外观中字形的数量、外观家族的名称、风格名称、 EM 大小等,详见 FT_FaceRec 定义。

4 FT_Size 类

每个 FT_Face 对象都有一个或多个 FT_Size 对象,一个尺寸对象用来存放指定字符宽度和高度的特定数据,每个新创建的外观对象有一个尺寸,可以通过 face->size 直接访问。
尺寸对象的内容可以通过调用 FT_Set_Pixel_Sizes()或 FT_Set_Char_Size()来改变。
一个新的尺寸对象可以通过 FT_New_Size()创建,通过 FT_Done_Size()销毁,一般客户程序无需做这一步,它们通常可以使用每个 FT_Face 缺省提供的尺寸对象。
FT_Size 公共属性定义在 FT_SizeRec 中,但是需要注意的是有些字体驱动定义它们自己的 FT_Size 的子类,以存储重要的内部数据,在每次字符大小改变时计算。大多数情况下,它们是尺寸特定的字体 hint。例如,TrueType 驱动存储 CVT 表,通过 cvt 程序执行将结果放入TT_Size 结构体中,而 Type1 驱动将scaled global
metrics 放在 T1_Size 对象中。

5 FT_GlyphSlot 类

字形槽的目的是提供一个地方,可以很容易地一个个地装入字形映象,而不管它的格式(位图、向量轮廓或其他)。理想的,一旦一个字形槽创建了,任何字形映象可以装入,无需其他的内存分配。在实际中,只对于特定格式才如此,像 TrueType,它显式地提供数据来计算一个槽地最大尺寸。
另一个字形槽地原因是他用来为指定字形保存格式特定的 hint,以及其他为正确装入字形的必要数据。基本的 FT_GlyphSlotRec 结构体只向客户程序展现了字形 metics 和映象,而真正的实现回包含更多的数据。例如,TrueType 特定的 TT_GlyphSlotRec 结构包含附加的属性,存放字形特定的字节码、在 hint 过程中暂时的轮廓和其他一些东西。
最后,每个外观对象有一个单一字形槽,可以用 face->glyph 直接访问。

3.函数

转载到字形槽得字形图像可以转换到一幅位图中,这可以在装载时使用 FT_LOAD_RENDER 标志或者调用 FT_Render_Glyph 函数实现。每一次你装载一个新的字形图像到字形槽,前面装载的将会从字形槽中抹去。但是,你可能需要从字形槽中提取这个图像,以用来在你的应用程序中缓存它,或者进行附加的变换,或者在转换到位图前测量它。 FreeType 2 API 有一个特殊的扩展能够以一种灵活和普通的方式处理字形图像。要使用它,你首先需要包含 FT_GLYPH_H 头文件,如下:
#include FT_GLYPH_H
现在我们将解释如何使用这个文件定义的这个函数。

1.把一个字符码转换为一个字形索引FT_Get_Char_Index函数

常,一个应用程序想通过字符码来装载它的字形图像。字符码是一个特定编码中代表该字符的数值。例如,字符码 64 代表了 ASCII 编码中的’A’。
一个 face 对象包含一个或多个字符表(charmap),字符表是用来转换字符码到字形索引的。例如,很多TrueType 字体包含两个字符表,一个用来转换 Unicode 字符码到字形索引,另一个用来转换 Apple Roman 编码到字形索引。这样的字体既可以用在 Windows(使用 Unicode)和 Macintosh(使用 Apple Roman)。同时要注意,一个特定的字符表可能没有覆盖完字体里面的全部字形。
当新建一个 face 对象时,它默认选择 Unicode 字符表。如果字体没包含 Unicode 字符表,FreeType 会尝试在字形名的基础上模拟一个。注意,如果字形名是不标准的那么模拟的字符表有可能遗漏某些字形。对于
某些字体,包括符号字体和旧的亚洲手写字体,Unicode 模拟是不可能的。我们将在稍后叙述如何寻找 face 中特定的字符表。现在我们假设 face 包含至少一个 Unicode 字符表,并且在调用FT_New_Face 时已经被选中。我们使用 FT_Get_Char_Index 把一个 Unicode 字符码转换为字形索引,
如下所示:

glyph_index = FT_Get_Char_Index( face, charcode );

这个函数会在 face 里被选中的字符表中查找与给出的字符码对应的字形索引。如果没有字符表被选中,这个函数简单的返回字符码。
注意,这个函数是 FreeType 中罕有的不返回错误码的函数中的一个。然而,当一个特定的字符码在 face
中没有字形图像,函数返回 0。按照约定,它对应一个特殊的字形图像――缺失字形,通常会显示一个框或一个空格。

2.从 face 中装载一个字形FT_Load_Glyph与FT_Render_Glyph函数

一旦你获得了字形索引,你便可以装载对应的字形图像。在不同的字体中字形图像存储为不同的格式。对于固定尺寸字体格式,如 FNT 或者 PCF,每一个图像都是一个位图。对于可伸缩字体格式,如 TrueType或者 Type1,使用名为轮廓(outlines)的矢量形状来描述每一个字形。一些字体格式可能有更特殊的途径来表示字形(如 MetaFont――但这个格式不被支持)。幸运的, FreeType2 有足够的灵活性,可以通过一个简单的API 支持任何类型的字形格式。
字形图像存储在一个特别的对象――字形槽(glyph slot)中。就如其名所暗示的,一个字形槽只是一个简单的容器,它一次只能容纳一个字形图像,可以是位图,可以是轮廓,或者其他。每一个 face 对象都有一个字形槽对象,可以通过 face->glyph 来访问。它的字段在 FT_GlyphSlotRec 结构的文档中解释了。
通过调用 FT_Load_Glyph 来装载一个字形图像到字形槽中,如下:

error = FT_Load_Glyph(
face, /* face 对象的句柄 */
glyph_index, /*  字形索引 */
load_flags ); /*  装载标志,参考下面 */

load_flags 的值是位标志集合,是用来指示某些特殊操作的。其默认值是 FT_LOAD_DEFAULT 即 0。
这个函数会设法从 face 中装载对应的字形图像:
• 如果找到一个对应该字形和象素尺寸的位图,那么它将会被装载到字形槽中。嵌入的位图总是比原生的
图像格式优先装载。因为我们假定对一个字形,它有更高质量的版本。这可以用 FT_LOAD_NO_BITMAP
标志来改变。
• 否则,将装载一个该字形的原生图像,把它伸缩到当前的象素尺寸,并且对应如 TrueType 和 Type1 这些
格式,也会完成 hinted 操作。
字段 face->glyph->format 描 述了字 形槽 中存 储的 字形 图像 的格 式。 如果 它的 值不 是FT_GLYPH_FORMAT_BITMAP,你可以通过 FT_Render_Glyph 把它直接转换为一个位图。如下:
error = FT_Render_Glyph( face->glyph, /* 字形槽 /render_mode ); / 渲染模式 */
render_mode 参数是一个位标志集合,用来指示如何渲染字形图像。把它设为 FT_RENDER_MODE_NORMAL渲染出一个高质量的抗锯齿(256 级灰度) 位图。这是默认情况,如果你想生成黑白位图, 可以使用FT_RENDER_MODE_MONO 标志。
一旦你生成了一个字形图像的位图,你可以通过 glyph->bitmap(一个简单的位图描述符)直接访问,同时用 glyph->bitmap_left 和 glyph->bitmap_top 来指定起始位置。
要注意,bitmap_left 是从字形位图当前笔位置到最左边界的水平距离,而 bitmap_top 是从笔位置(位于基线)到最高边界得垂直距离。他么是正数,指示一个向上的距离。下一部分将给出字形槽内容的更多细节,以及如何访问特定的字形信息(包括度量)。

3.提取字形图像FT_Get_Glyph函数

提取字形图像

你可以很简单地提取一个字形图像。这里有一向代码向你展示如何去做:

FT_Glyph glyph; /*  字形图像的句柄 */
...
error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NORMAL );
if ( error ) { ... }
error = FT_Get_Glyph( face->glyph, &glyph );
if ( error ) { ... }

• 创建一个类型为 FT_Glyph,名为glyph 的变量。这是一个字形图像的句柄(即指针)。
• 装载字形图像(通常情况下)到 face 的字形槽中。我们不使用 FT_LOAD_RENDER 因为我们想抓取一个
可缩放的字形图像,以便后面对其进行变换。
• 通过调用 FT_Get_Glyph,把字形图像从字形槽复制到新的 FT_Glyph 对象 glyph 中。这个函数返回一个错误码并且设置 glyph。
要非常留意,被取出的字形跟字形槽中的原始字形的格式是一样的。例如,如果我们从 TrueType 字体文件中装载一个字形,字形图像将是可伸缩的矢量轮廓。 如果你想知道字形是如何模型和存储的,你可以访问 flyph->format 字段。一个新的字形对象可以通过调用FT_Done_Glyph 来销毁。 字形对象正好包含一个字形图像和一个 2D 矢量,2D 矢量以 16.16 固定浮点坐标的形式表示字形的推进。后者可以直接通过glyph->advance 访问。 注意,不同于其他 TrueType 对象,库不保存全部分配了的字形对象的列表。这意味着你必须自己销毁它们,而不是依靠 FT_Done_FreeType 完成全部的清除。

4.变换和复制字形图像FT_Glyph_Transform函数

如果字形图像是可伸缩的(例如,如果 glyph->format 不等于 FT_GLYPH_FORMAT_BITMAP),那么就可以随时通过调用 FT_Glyph_Transform 来变换该图像。
你也可以通过 FT_Glyph_Copy 复制一个字形图像。这里是一些例子代码:

FT_Glyph glyph, glyph2;
FT_Matrix matrix;
FT_Vector delta;
...  装载字形图像到 `glyph' ...
/*  复制 glyph  到 glyph2 */
error = FT_Glyph_Copy( glyph, &glyph2 );
if ( error ) { ...  无法复制(内存不足) ... }
/*  平移 `glyph' */
delta.x = -100 * 64; /*  坐标是 26.6  象素格式的 */
delta.y = 50 * 64;
FT_Glyph_Transform( glyph, 0, &delta );
/*  变换 glyph2  (水平剪切) */
matrix.xx = 0x10000L;
matrix.xy = 0.12 * 0x10000L;
matrix.yx = 0;
matrix.yy = 0x10000L;
FT_Glyph_Transform( glyph2, &matrix, 0 );

注意, 2x2 矩阵变换总是适用于字形的 16.16 推进矢量,所以你不需要重修计算它。

5.测量字形图像FT_Glyph_Get_CBox函数

你也可以通过 FT_Glyph_Get_CBox 函数检索任意字形图像(无论是可伸缩或者不可伸缩的)的控制(约束)框,如下:

FT_BBox bbox;
...
FT_Glyph_Get_CBox( glyph, bbox_mode, &bbox );

坐标是跟字形的原点(0, 0)相关的,使用 y 向上的约定。这个函数取一个特殊的参数: bbox_mode 来指出如何表示框坐标。 如果字形装载时使用了 FT_LOAD_NO_SCALE 标志, bbox_mode 必须设置为FT_GLYPH_BBOX_UNSCALED , 以 此 来 获 得 以 26.6 象 素 格 式 为 单 位 表 示 的 不 可 缩 放 字 体 。 值FT_GLYPH_BBOX_SUBPIXELS 是这个常量的另一个名字。 要注意,框(box)的最大坐标是唯一的,这意味着你总是可以以整数或 26.6 象素的形式计算字形图像的宽度和高度,公式如下:

width = bbox.xMax - bbox.xMin;
height = bbox.yMax - bbox.yMin;

同时要注意,对于 26.6 坐标,如果 FT_GLYPH_BBOX_GRIDFIT 被用作为 bbox_mode,坐标也将网格对齐,
符合如下公式:

bbox.xMin = FLOOR( bbox.xMin )
bbox.yMin = FLOOR( bbox.yMin )
bbox.xMax = CEILING( bbox.xMax )
bbox.yMax = CEILING( bbox.yMax )

要把 bbox 以整数象素坐标的形式表示,把 bbox_mode 设置为 FT_GLYPH_BBOX_TRUNCATE。最后,要把约束框以网格对齐象素坐标的形式表示,把 bbox_mode 设置为 FT_GLYPH_BBOX_PIXELS。

6.转换字形图像为位图FT_Glyph_To_Bitmap函数

当你已经把字形对象缓存或者变换后,你可能需要转换它到一个位图。这可以通过 FT_Glyph_To_Bitmap函数简单得实现。它负责转换任何字形对象到位图,如下:

FT_Vector origin;
origin.x = 32; /* 26.6 格式的 1/2 象素 */
origin.y = 0;
error = FT_Glyph_To_Bitmap(
&glyph,
render_mode,
&origin,
1 ); /*  销毁原始图像 == true */

• 第一个参数是源字形句柄的地址。当这个函数被调用时,它读取该参数来访问源字形对象。调用结束后,这个句柄将指向一个新的包含渲染后的位图的字形对象。
• 第二个参数时一个标准渲染模式,用来指定我们想要哪种位图。它取 FT_RENDER_MODE_DEFAULT 时表示 8 位颜色深度的抗锯齿位图;它取 FT_RENDER_MODE_MONO 时表示 1 位颜色深度的黑白位图。
• 第三个参数是二维矢量的指针。该二维矢量是在转换前用来平移源字形图像的。要注意,函数调用后源图像将被平移回它的原始位置(这样便不会有变化)。如果你在渲染前不需要平移源字形,设置这个指针
为 0。
• 最后一个参数是一个布尔值,用来指示该函数是否要销毁源字形对象。如果为 false,源字形对象不会被销毁,即使它的句柄丢失了(客户应用程序需要自己保留句柄)。
如果没返回错误,新的字形对象总是包含一个位图。并且你必须把它的句柄进行强制类型转换,转换为FT_BitmapGlyph 类型,以此访问它的内容。这个类型是 FT_Glyph 的一种“子类”,它包含下面的附加字段(看 FT_BitmapGlyphRec):
Left
类似于字形槽的 bitmap_left 字段。这是字形原点(0,0)到字形位图最左边象素的水平距离。它以整数象素的形式表示。
Top
类似于字形槽的 bitmap_top 字段。它是字形原点(0,0)到字形位图最高象素之间的垂直距离(更精确来说,到位图上面的象素)。这个距离以整数象素的形式表示,并且 y 轴向上为正。
Bitmap
这是一个字形对象的位图描述符,就像字形槽的 bitmap 字段。

实例代码

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <wchar.h>

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

typedef struct TGlyph_ { 
    FT_UInt index; /* glyph index */ 
    FT_Vector pos; /* glyph origin on the baseline */ 
    FT_Glyph image; /* glyph image */ 
} TGlyph, *PGlyph; 


#define MAX_GLYPHS  100

int fd_fb;
struct fb_var_screeninfo var;   /* Current var */
struct fb_fix_screeninfo fix;   /* Current fix */
int screen_size;
unsigned char *fbmem;
unsigned int line_width;
unsigned int pixel_width;


/* color : 0x00RRGGBB */
void lcd_put_pixel(int x, int y, unsigned int color)
{
    unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width;
    unsigned short *pen_16; 
    unsigned int *pen_32;   

    unsigned int red, green, blue;  

    pen_16 = (unsigned short *)pen_8;
    pen_32 = (unsigned int *)pen_8;

    switch (var.bits_per_pixel)
    {
        case 8:
        {
            *pen_8 = color;
            break;
        }
        case 16:
        {
            /* 565 */
            red   = (color >> 16) & 0xff;
            green = (color >> 8) & 0xff;
            blue  = (color >> 0) & 0xff;
            color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
            *pen_16 = color;
            break;
        }
        case 32:
        {
            *pen_32 = color;
            break;
        }
        default:
        {
            printf("can't surport %dbpp\n", var.bits_per_pixel);
            break;
        }
    }
}



/* 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;

    //printf("x = %d, y = %d\n", x, y);

  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 >= var.xres || j >= var.yres )
        continue;

      //image[j][i] |= bitmap->buffer[q * bitmap->width + p];
      lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]);
    }
  }
}


int Get_Glyphs_Frm_Wstr(FT_Face face, wchar_t * wstr, TGlyph glyphs[])
{
    int n;
    PGlyph glyph = glyphs;
    int pen_x = 0;
    int pen_y = 0;
    int error;
    FT_GlyphSlot  slot = face->glyph;;
    
        
    for (n = 0; n < wcslen(wstr); n++)
    {
        glyph->index = FT_Get_Char_Index( face, wstr[n]); 
        /* store current pen position */ 
        glyph->pos.x = pen_x; 
        glyph->pos.y = pen_y;       

        /* load时是把glyph放入插槽face->glyph */
        error = FT_Load_Glyph(face, glyph->index, FT_LOAD_DEFAULT);
        if ( error ) 
            continue;

        error = FT_Get_Glyph(face->glyph, &glyph->image ); 
        if ( error ) 
            continue;

        /* translate the glyph image now */ 
        /* 这使得glyph->image里含有位置信息 */
        FT_Glyph_Transform(glyph->image, 0, &glyph->pos );

        pen_x += slot->advance.x;  /* 1/64 point */

        /* increment number of glyphs */ 
        glyph++;        
    }

    /* count number of glyphs loaded */ 
    return (glyph - glyphs);
}

void compute_string_bbox(TGlyph glyphs[], FT_UInt num_glyphs, FT_BBox *abbox )
{
    FT_BBox bbox; 
    int n;
    
    bbox.xMin = bbox.yMin = 32000; 
    bbox.xMax = bbox.yMax = -32000;

    for ( n = 0; n < num_glyphs; n++ )
    {
        FT_BBox glyph_bbox;
        
        FT_Glyph_Get_CBox(glyphs[n].image, FT_GLYPH_BBOX_TRUNCATE, &glyph_bbox );

        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;
    }

    *abbox = bbox;
}


void Draw_Glyphs(TGlyph glyphs[], FT_UInt num_glyphs, FT_Vector pen)
{
    int n;
    int error;
    
    for (n = 0; n < num_glyphs; n++)
    {
        FT_Glyph_Transform(glyphs[n].image, 0, &pen);
        /* convert glyph image to bitmap (destroy the glyph copy!) */ 
        error = FT_Glyph_To_Bitmap(&glyphs[n].image, FT_RENDER_MODE_NORMAL, 0, /* no additional translation */ 
                                    1 );        /* destroy copy in "image" */
        if ( !error ) 
        { 
            FT_BitmapGlyph bit = (FT_BitmapGlyph)glyphs[n].image; 
            draw_bitmap(&bit->bitmap, bit->left, var.yres - bit->top); 
            FT_Done_Glyph(glyphs[n].image ); 
        }
    }
}

int main(int argc, char **argv)
{
    wchar_t *wstr1 = L"个人博客P-Paranoid";
    wchar_t *wstr2 = L"https://blog.csdn.net/weixin_52849254";

    FT_Library    library;
    FT_Face       face;
    int error;
    FT_Vector     pen;
    FT_GlyphSlot  slot;
    int i;
    FT_BBox bbox;

    int line_box_ymin = 10000;
    int line_box_ymax = 0;

    int line_box_width;
    int line_box_height;

    TGlyph glyphs[MAX_GLYPHS]; /* glyphs table */ 
    FT_UInt num_glyphs;


    if (argc != 2)
    {
        printf("Usage : %s <font_file>\n", argv[0]);
        return -1;
    }
        

    fd_fb = open("/dev/fb0", O_RDWR);
    if (fd_fb < 0)
    {
        printf("can't open /dev/fb0\n");
        return -1;
    }

    if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
    {
        printf("can't get var\n");
        return -1;
    }

    if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix))
    {
        printf("can't get fix\n");
        return -1;
    }

    line_width  = var.xres * var.bits_per_pixel / 8;
    pixel_width = var.bits_per_pixel / 8;
    screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
    fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
    if (fbmem == (unsigned char *)-1)
    {
        printf("can't mmap\n");
        return -1;
    }

    /* 清屏: 全部设为黑色 */
    memset(fbmem, 0, screen_size);

    /* 显示矢量字体 */
    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 */    
    slot = face->glyph;

    FT_Set_Pixel_Sizes(face, 24, 0);

    /* wstr1 */
    num_glyphs = Get_Glyphs_Frm_Wstr(face, wstr1, glyphs);
    
    compute_string_bbox(glyphs, num_glyphs, &bbox);
    line_box_width  = bbox.xMax - bbox.xMin;
    line_box_height = bbox.yMax - bbox.yMin;

    pen.x = (var.xres - line_box_width)/2 * 64;
    pen.y = (var.yres - line_box_height)/2 * 64;

    Draw_Glyphs(glyphs, num_glyphs, pen);

    /* wstr2 */
    num_glyphs = Get_Glyphs_Frm_Wstr(face, wstr2, glyphs);

    compute_string_bbox(glyphs, num_glyphs, &bbox);
    line_box_width  = bbox.xMax - bbox.xMin;
    line_box_height = bbox.yMax - bbox.yMin;

    pen.x = (var.xres - line_box_width)/2 * 64;
    pen.y = pen.y - 24 * 64;
    Draw_Glyphs(glyphs, num_glyphs, pen);


    return 0;   
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/486425.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

银行家算法--申请资源

银行家算法–申请资源 问题描述&#xff1a; 输入N个进程(N<100)&#xff0c;以及M类资源&#xff08;M<100&#xff09;&#xff0c;初始化各种资源的总数&#xff0c;T0时刻资源的分配情况。例如&#xff1a; 假定系统中有5个进程{P0&#xff0c;P1&#xff0c;P2&…

文字的显示

文字的显示 文章目录 文字的显示1.文字编码方式2.英文和汉字的点阵显示3.显示中文“中”和“A”show_font.c结果 1.文字编码方式 数字>代表什么->显示为什么 GBK国标拓展 下列代码用不同编码方式保存utf-8.c ansi.c #include <stdio.h>int main(int argc ,char *…

MyBaits的注解开发

为什么要用MyBaits注解开发&#xff1f; 前面介绍了MyBatis的基本用法、关联映射、动态SQL和缓存机制等知识&#xff0c;所有的配置都是基于XML文件完成的&#xff0c;但在实际开发中&#xff0c;大量的XML配置文件的编写是非常繁琐的&#xff0c;为此&#xff0c;MyBatis提供…

《Netty》从零开始学netty源码(五十一)之PoolArena的内存释放与扩容

目录 释放空间free()destroyChunk()freeChunk() 扩容reallocate()memoryCopy() 释放空间free() 当chunk使用完毕后需要释放&#xff0c;如果是池化chunk则需要放回池中供下次循环利用&#xff0c;如果是非池化的则直接释放对应的物理空间。 下面继续分析释放具体物理空间的des…

代码随想录算法训练营(总结)|动态规划总结篇

一、动态规划五部曲 确定dp及其下标的含义确定递推关系式初始化值确定遍历顺序验证 二、01背包问题 1. 基本理解 理解&#xff1a;所谓的01背包问题&#xff0c;其关键在于物品只能放入1次&#xff0c;不能够重复利用&#xff0c;因此称呼为01背包问题。与完全背包的区别在…

【深度学习】计算机视觉(十)——Faster RCNN(实践篇)

文章目录 写在最前面 step1&#xff1a;相关说明step2&#xff1a;Prerequisites安装要求tensorflow-gpu1. 安装和测试2. 错误提示① numpy版本② tensorflow版本③TensorRT step3&#xff1a;Installation1. Update your -arch2. Install the Python COCO API3. setup VOC4. D…

【VM服务管家】VM4.2平台SDK_6.4 结果获取类

目录 2.4.1 数据结果&#xff1a;通过流程输出或者模块输出获取数据结果的方法2.4.2 流程回调&#xff1a;某个流程运行开始与结束的回调方法 2.4.1 数据结果&#xff1a;通过流程输出或者模块输出获取数据结果的方法 描述 环境&#xff1a;VM4.2 VS2013及以上 现象&#xff…

从不自量力到 AI 助力,我如何翻译完一整本英文书

披露和声明&#xff1a; 本文提及的翻译作品系使用 AI 技术制作&#xff0c;并经人工调整&#xff0c;具体步骤如文中所述。本文在后期修改过程中使用 ChatGPT 辅助&#xff0c;目的为精简原版中的口语化表述。我的英文水平有限&#xff0c;翻译这本书主要是为了自我学习&…

自动驾驶— Planning - Udacity Self-Driving Car Engineer

第六讲规划 6.1规划简介 在规划中&#xff0c;我们结合了高清地图、定位和预测来构建车辆的轨迹。规划的第一步是路线导航&#xff0c;重点是如何在地图上从A到B。路由以地图数据作为输入&#xff0c;并输出可导航路径。手机上的导航系统就是路线规划的一个例子。在Apollo中&…

高性能——零拷贝

目录 背景 零拷贝定义 传统I/O执行流程 背景 零拷贝算是一个老生常谈的问题啦&#xff0c;很多顶级框架都用到了零拷贝来提升性能&#xff0c;比如我们经常接触到的Kafka 、RocketMQ、Netty 。 零拷贝定义 “拷贝”:就是指数据从一个存储区域转移到另一个存储区域.“零”…

games103——作业2

实验二主要使用隐式积分法以及PBD法完成布料仿真 完整项目已上传至github。 文章目录 基于物理的方法弹簧系统单个弹簧多个弹簧弹簧网络结构化弹簧网络(Structured Spring Networks)非结构化弹簧网络(Unstructured Spring Networks)三角网格表示 代码 求解质量弹簧系统的显示积…

UG NX二次开发(C++)-用UF_OBJ_cycle_objs_in_part遍历对象

文章目录 1、前言2、在UG NX中创建多个体3、创建UF_OBJ_cycle_objs_in_part的使用函数3.1 首先声明函数&#xff1a;3.2定义函数代码3.3 函数调用代码3.4 测试结果 1、前言 UG NX二次开发中&#xff0c;比如体、面、边等&#xff0c;在NXOpen中可以通过Collection来实现&#…

华硕ROG|玩家国度冰刃6双屏GX650RX Windows11原厂预装系统 工厂模式恢复安装带ASUSRecevory一键还原

华硕ROG|玩家国度 冰刃6双屏 GX650RX Windows11原厂预装系统 工厂模式恢复安装带ASUSRecevory一键还原 文件地址&#xff1a;https://pan.baidu.com/s/1snKOsH3OMl3GZLqeAf-GLA?pwd8888 华硕工厂恢复系统 &#xff0c;安装结束后带隐藏分区以及机器所有驱动软件 需准备一个…

【告警通知】Java项目统一异常捕获处理【钉钉告警推送消息】

基础描述 在实际业务中&#xff0c;很多时候都是被动获取系统业务异常&#xff0c;如通过业务部门或者通过客户反馈说某个功能不行时&#xff0c;这样显得系统很被动和呆板在SpringBoot中有统一异常处理可以来实现&#xff0c;当我们检测到非业务异常时&#xff0c;比如空指针…

《SpringBoot中间件设计与实战》第2章 服务治理,超时熔断

需求背景 在流量较大的场景下,举个例子,用户在电商平台下单后开始跳转到在线收银台进行支付。由于支付渠道和网络环境随时都有可能发生问题,那么你该怎么保证支付系统的可靠性呢? 保证支付系统的可靠性需要考虑的点非常多,但这里有一个最直接和重点的内容就支付响应时长…

【王道·计算机网络】第三章 数据链路层【未完】

一、功能 研究思想&#xff1a;水平方向个数据链路层的差距 1.1 数据链路层基本概念 结点&#xff1a;主机、路由器链路&#xff1a;网络中两个节点之间的物理通道&#xff0c;传输介质包含&#xff1a;双绞线、光纤、微波。分为&#xff1a;有线链路、无线链路数据链路&…

TIM输入不捕获-STM32

TIM输入不捕获-STM32 IC(Input Capture) 输入捕获 输入捕获模式下&#xff0c;当通道输入引脚出现指定电平跳变时&#xff0c;当前CNT的值将被锁存到CCR中&#xff0c;可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数 每个高级定时器和通用定时器都拥有4个输入…

【Linux】进程学习(2)---理解进程操作

文章目录 查看进程通过系统目录查看通过ps命令查看 通过系统调用获取进程标识符通过系统调用创建进程初识fork函数fork函数的返回值 进程状态阻塞与运行状态Linux内核源码中的进程状态运行状态-R浅度睡眠状态-S深度睡眠状态-D暂停状态-T僵尸状态-Z死亡状态-X 查看进程 通过系统…

操作系统引导(开机过程)

操作系统安装在C盘中&#xff0c;其一步步启动的过程如下&#xff1a; 操作系统要启动&#xff0c;操作系统的数据需要先被放入主存里。 如图所示&#xff0c;计算机的主存由RAM和ROM组成&#xff0c;ROM芯片被集成在电脑主板上&#xff0c;里面存储的是BIOS&#xff08;Basic…

【组合数学算贡献+枚举】CF816div2 C. Monoblock

题解都看了半天才懂 Problem - C - Codeforces 题意&#xff1a; 思路&#xff1a; 一开始的思路是这样的&#xff1a; 只能说&#xff0c;想到了更换枚举对象&#xff0c;然后组合数学算贡献 也想到了修改操作与&#xff08;a[i]和a[i-1]&#xff09;有关 但是我想的是枚…