当前位置: 首页 > news >正文

u8g2原理解析

u8g2原理解析

  • u8g2_font_info_t 结构体用于存储 U8g2 图形库中字体的相关信息.
    • 1. 字符计数与编码模式相关信息(偏移量 0)
    • 2. 字符尺寸编码位数信息(偏移量 4)
    • 3. 字符最大尺寸与偏移信息(偏移量 9)
    • 4. 字符上升和下降高度信息(偏移量 13)
    • 5. 字符起始位置信息(偏移量 17)
    • 6. Unicode 字符起始位置信息(偏移量 21,可选)
  • u8g2_draw_string
  • UFT8解码函数(将中文变成UFT8解码)
    • 一个例子**“世界” 的 Unicode 编码及解析过程**

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 编码)。

相关文章:

  • Java中Math.random()的用法
  • 2025-03-15 学习记录--C/C++-PTA 练习3-4 统计字符
  • Java创造型模式之原型模式详解
  • 《基於Python的网络爬虫抓包技术研究与应用》
  • 基于“动手学强化学习”的知识点(一):第 14 章 SAC 算法(gym版本 >= 0.26)
  • 基本的WinDbg调试指令
  • SEO优先级矩阵:有限资源下的ROI最大化决策模型
  • 科技快讯 | “垃圾短信”可以被识别了;阿里正式推出AI旗舰应用;OpenAI深夜发布全新Agent工具
  • python数据分析文件夹篇--pandas,openpyxl,xlwings三种方法批量创建、 复制、删除工作表
  • JAVA中的多态性以及它在实际编程中的作用
  • 1141. 【贪心算法】排队打水
  • 【2025最新版】如何将fnm与node.js安装在D盘?【保姆级安装及人性话理解教程】
  • git submodule
  • 疗养院管理系统设计与实现(代码+数据库+LW)
  • 动态规划习题代码题解
  • 本地部署量化满血版本deepseek的Ktransformer清华方案的硬件配置
  • 【linux驱动开发】创建proc文件系统中的目录和文件实现
  • win10 win+shift+s 无法立即连续截图 第二次截图需要等很久
  • [RA-L 2023] Coco-LIC:基于非均匀 B 样条的连续时间紧密耦合 LiDAR-惯性-相机里程计
  • API自动化测试实战:Postman + Newman/Pytest的深度解析
  • 复旦大学艺术馆开馆:以当代视角再看文科文脉
  • 2025全球城市科技传播能力指数出炉,上海位列第六
  • 小米汽车回应部分SU7前保险杠形变
  • 新任重庆市垫江县委副书记刘振已任县政府党组书记
  • 没有握手,采用翻译:俄乌三年来首次直接会谈成效如何?
  • 国家防汛抗旱总指挥部对15个重点省份开展汛前实地督导检查