目录

u8g2原理解析

u8g2原理解析

u8g2_font_info_t 结构体用于存储 U8g2 图形库中字体的相关信息.

1. 字符计数与编码模式相关信息(偏移量 0)

uint8_t glyph_cnt;
uint8_t bbx_mode;
uint8_t bits_per_0;
uint8_t bits_per_1;

glyph_cnt:

含义:表示字体中可用字符(字形,glyph)的数量。这有助于确定字体所能支持的字符范围。

用途:在遍历字体中的字符时,可根据此值来控制循环范围。

bbx_mode:

含义:边界框(bounding box)模式。边界框用于定义字符的最小矩形区域,此模式决定了如何计算和使用这些边界框。

用途:在进行字符布局和对齐时,根据不同的模式来确定字符的位置和大小。

bits_per_0 和 bits_per_1:

含义:分别表示编码中 “0” 和 “1” 所占用的位数。在字体的点阵数据编码中,不同的编码方式可能对 “0” 和 “1” 的表示位数有不同要求。

用途:在解码字体点阵数据时,根据这些位数信息来正确解析数据。

2. 字符尺寸编码位数信息(偏移量 4)

uint8_t bits_per_char_width;
uint8_t bits_per_char_height;
uint8_t bits_per_char_x;
uint8_t bits_per_char_y;
uint8_t bits_per_delta_x;
bits_per_char_width

含义:表示字符宽度编码所占用的位数。

用途:在读取字体数据中字符宽度信息时,根据此位数进行解析。

bits_per_char_height:

含义:表示字符高度编码所占用的位数。

用途:同理,用于解析字符高度信息。

bits_per_char_x 和 bits_per_char_y:

含义:分别表示字符在 X 轴和 Y 轴上位置编码所占用的位数。

用途:用于确定字符在字体中的具体位置。

bits_per_delta_x:

含义:表示字符在 X 轴上的偏移量编码所占用的位数。

用途:在进行字符布局时,根据此偏移量来调整字符之间的间距。

3. 字符最大尺寸与偏移信息(偏移量 9)

int8_t max_char_width;
int8_t max_char_height; 
int8_t x_offset;
int8_t y_offset;
max_char_width

含义:字体中字符的最大宽度。

用途:在进行文本布局时,可根据此值来估算一行文本所需的最大宽度。

max_char_height:

含义:字体中字符的最大高度,注意这里是整体高度,而不是上升高度(ascent)。上升高度等于 max_char_height + y_offset。

用途:用于确定文本行的高度。

x_offset 和 y_offset:

含义:分别表示字符在 X 轴和 Y 轴上的偏移量。

用途:在绘制字符时,根据这些偏移量来调整字符的位置。

4. 字符上升和下降高度信息(偏移量 13)

int8_t  ascent_A;
int8_t  descent_g;
int8_t  ascent_para;
int8_t  descent_para;

ascent_A:

含义:大写字母 “A” 的上升高度,即字符从基线(baseline)到最高点的距离。

用途:在文本布局中,用于确定文本行的顶部位置。

descent_g:

含义:小写字母 “g” 的下降高度,通常为负值,表示字符从基线到最低点的距离。

用途:用于确定文本行的底部位置。

ascent_para 和 descent_para:

含义:分别表示段落的上升高度和下降高度,可能用于处理多行文本的布局。

用途:在进行段落排版时,根据这些值来调整行间距和段落的整体布局。

5. 字符起始位置信息(偏移量 17)

uint16_t start_pos_upper_A;
uint16_t start_pos_lower_a; 
start_pos_upper_A

含义:大写字母 “A” 在字体数据中的起始位置。

用途:在查找大写字母 “A” 的点阵数据时,根据此位置进行定位。

start_pos_lower_a:

含义:小写字母 “a” 在字体数据中的起始位置。

用途:同理,用于查找小写字母 “a” 的点阵数据。

6. Unicode 字符起始位置信息(偏移量 21,可选)

#ifdef U8G2_WITH_UNICODE  
uint16_t start_pos_unicode;
#endif

start_pos_unicode:

含义:如果启用了 Unicode 支持(U8G2_WITH_UNICODE 宏定义),此成员表示 Unicode 字符在字体数据中的起始位置。

用途:在处理 Unicode 字符时,根据此位置来查找相应的点阵数据。

综上所述,u8g2_font_info_t 结构体通过存储字体的各种信息,为 U8g2 库在文本渲染和布局过程中提供了必要的参数,确保能够正确地显示和排版文本。

u8g2_draw_string

函数定义和参数

static u8g2_uint_t u8g2_draw_string(u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, const char *str)

static:表示该函数是一个静态函数,其作用域仅限于当前文件。

返回值:u8g2_uint_t 类型,返回绘制整个字符串所占用的水平或垂直总长度(根据字体旋转方向而定)。

参数:

u8g2_t *u8g2:指向 u8g2_t 结构体的指针,该结构体包含了 U8g2 库的上下文信息,如显示屏设置、字体信息等。

u8g2_uint_t x 和 u8g2_uint_t y:指定字符串绘制的起始坐标。

const char *str:指向要绘制的字符串的指针。

变量声明

uint16_t e;
u8g2_uint_t delta, sum;

e:用于存储从字符串中解码出的 Unicode 字符编码。

delta:表示绘制单个字符所占用的水平或垂直长度。

sum:用于累加绘制整个字符串所占用的总长度。

UTF - 8 初始化

u8x8_utf8_init(u8g2_GetU8x8(u8g2));

调用 u8x8_utf8_init 函数对 UTF - 8 解码进行初始化,确保后续能正确处理 UTF - 8 编码的字符串。u8g2_GetU8x8(u8g2) 用于获取 u8g2 结构体中关联的 u8x8 上下文。

初始化总长度

sum = 0;

将 sum 初始化为 0,用于后续累加字符绘制的长度。

字符串处理循环

for(;;)
{
    e = u8g2->u8x8.next_cb(u8g2_GetU8x8(u8g2), (uint8_t)*str);
    if ( e == 0x0ffff )
        break;
    str++;

u8g2->u8x8.next_cb 是一个回调函数,用于从字符串中解码出下一个 Unicode 字符编码。

如果解码结果为 0x0ffff,表示已经到达字符串的末尾,此时跳出循环。

每次解码后,将字符串指针 str 向后移动一位。

绘制字符

if ( e != 0x0fffe )
{
    delta = u8g2_DrawGlyph(u8g2, x, y, e);

如果解码结果不是 0x0fffe(表示无效字符),则调用 u8g2_DrawGlyph 函数在指定位置 (x, y) 绘制该 Unicode 字符。该函数返回绘制该字符所占用的水平或垂直长度,存储在 delta 中。

处理字体旋转

#ifdef U8G2_WITH_FONT_ROTATION
    switch(u8g2->font_decode.dir)
    {
        case 0:
            x += delta;
            break;
        case 1:
            y += delta;
            break;
        case 2:
            x -= delta;
            break;
        case 3:
            y -= delta;
            break;
    }

    /*
    // requires 10 bytes more on avr
    x = u8g2_add_vector_x(x, delta, 0, u8g2->font_decode.dir);
    y = u8g2_add_vector_y(y, delta, 0, u8g2->font_decode.dir);
    */

#else
    x += delta;
#endif

启用字体旋转支持(U8G2_WITH_FONT_ROTATION 宏定义):

根据 u8g2->font_decode.dir 的值(表示字体旋转方向),更新下一个字符的绘制位置。

case 0:正常水平方向,x 坐标增加 delta。

case 1:顺时针旋转 90 度,y 坐标增加 delta。

case 2:顺时针旋转 180 度,x 坐标减少 delta。

case 3:顺时针旋转 270 度,y 坐标减少 delta。

注释掉的代码 u8g2_add_vector_x 和 u8g2_add_vector_y 是另一种实现方式,但会增加 AVR 平台的代码大小。

未启用字体旋转支持:默认情况下,水平方向绘制,x 坐标增加 delta。

累加总长度

sum += delta;    

将当前字符绘制所占用的长度 delta 累加到 sum 中。

返回总长度

return sum;

循环结束后,返回绘制整个字符串所占用的总长度。

UFT8解码函数(将中文变成UFT8解码)

uint16_t u8x8_utf8_next(u8x8_t *u8x8, uint8_t b)
{
  if ( b == 0 || b == '\n' )	/* '\n' terminates the string to support the string list procedures */
    return 0x0ffff;	/* end of string detected, pending UTF8 is discarded */
  if ( u8x8->utf8_state == 0 )
  {
    if ( b >= 0xfc )	/* 6 byte sequence */
    {
      u8x8->utf8_state = 5;
      b &= 1;
    }
    else if ( b >= 0xf8 )
    {
      u8x8->utf8_state = 4;
      b &= 3;
    }
    else if ( b >= 0xf0 )
    {
      u8x8->utf8_state = 3;
      b &= 7;      
    }
    else if ( b >= 0xe0 )
    {
      u8x8->utf8_state = 2;
      b &= 15;
    }
    else if ( b >= 0xc0 )
    {
      u8x8->utf8_state = 1;
      b &= 0x01f;
    }
    else
    {
      /* do nothing, just use the value as encoding */
      return b;
    }
    u8x8->encoding = b;
    return 0x0fffe;
  }
  else
  { 
    u8x8->utf8_state--;
    /* The case b < 0x080 (an illegal UTF8 encoding) is not checked here. */
    u8x8->encoding<<=6;
    b &= 0x03f;
    u8x8->encoding |= b;
    if ( u8x8->utf8_state != 0 )
      return 0x0fffe;	/* nothing to do yet */
  }
  return u8x8->encoding;
}

一个例子**“世界” 的 Unicode 编码及解析过程**

  1. Unicode 编码结果

    世:U+4E16

    界:U+754C

  2. UTF-8 编码解析过程

    根据 UTF-8 编码规则,将 Unicode 代码点转换为字节序列的步骤如下:

    “世”(U+4E16)的编码

    代码点转换为二进制

    U+4E16 → 二进制 0100 1110 0001 0110。

    按三字节格式拆分

    UTF-8 三字节格式为:

    plaintext

    1110xxxx 10xxxxyy 10yyyyyy

将二进制位分配到各字段:

第一部分(4 位):0100

第二部分(6 位):111000

第三部分(6 位):010110

生成字节序列

第一字节:1110 0100 → 0xE4

第二字节:10 111000 → 0xB8

第三字节:10 010110 → 0x96

最终 UTF-8 编码:E4 B8 96。

“界”(U+754C)的编码

代码点转换为二进制

U+754C → 二进制 0111 0101 0100 1100。

按三字节格式拆分

第一部分(4 位):0111

第二部分(6 位):010101

第三部分(6 位):001100

生成字节序列

第一字节:1110 0111 → 0xE7

第二字节:10 010101 → 0x95

第三字节:10 001100 → 0x8C

最终 UTF-8 编码:E7 95 8C。

3. 结合u8x8_utf8_next函数的处理逻辑

当函数依次处理 “世界” 的 UTF-8 字节E4 B8 96 E7 95 8C时:

处理E4(0xE4)

b = 0xE4,判断为三字节序列(0xE0 ≤ b < 0xF0)。

utf8_state设为 2,encoding保存0x16(E4的低 4 位),返回0x0fffe。

处理B8(0xB8)

utf8_state减为 1,encoding左移 6 位,合并B8的低 6 位(0x18)。

encoding = 0x16 « 6 | 0x18 = 0x1618,返回0x0fffe。

处理96(0x96)

utf8_state减为 0,encoding左移 6 位,合并96的低 6 位(0x16)。

encoding = 0x1618 « 6 | 0x16 = 0x4E16,返回0x4E16(“世” 的 Unicode 编码)。

处理E7(0xE7)

重复上述步骤,最终返回0x754C(“界” 的 Unicode 编码)。