diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e907a6..be24ff4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,4 +23,6 @@ add_executable(HW_Lib main.c ${SOURCES}) #导入库 add_subdirectory(lib) -target_link_libraries(HW_Lib HW_LIB_List HW_LIB_Task HW_LIB_Printf HW_LIB_Utils HW_LIB_Iic HW_LIB_Spi HW_LIB_Key HW_LIB_Oled) \ No newline at end of file +target_link_libraries(HW_Lib HW_LIB_List HW_LIB_Task HW_LIB_Printf HW_LIB_Utils HW_LIB_Iic + HW_LIB_Spi HW_LIB_Key HW_LIB_Oled HW_LIB_Font +) \ No newline at end of file diff --git a/demo/oled/test.c b/demo/oled/test.c index 2288c26..c54e0bb 100644 --- a/demo/oled/test.c +++ b/demo/oled/test.c @@ -114,7 +114,7 @@ void Test_OLED() { int s = 0; char buf[30] = {0x5B, 0x57, 0x5E, 0x93, 0x75, 0x1F, 0x62, 0x10, 0x6D, 0x4B, 0x8B, 0xD5}; - OLED_ShowCHString(&oled, 15, 16, buf, 16); + OLED_ShowCHString(&oled, 15, 16, buf); OLED_Refresh(&oled); _beginthread(Get_Key, 0, NULL); diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 9fb9763..7e81f2c 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -8,6 +8,7 @@ set(LIBRARIES HW_LIB_Spi src/spi inc/spi HW_LIB_Key src/key inc/key HW_LIB_Oled src/oled inc/oled + HW_LIB_Font src/font inc/font ) # 循环浏览库列表以创建它们 diff --git a/lib/inc/font/lvgl.h b/lib/inc/font/lvgl.h new file mode 100644 index 0000000..848b991 --- /dev/null +++ b/lib/inc/font/lvgl.h @@ -0,0 +1,224 @@ + +#pragma once + +#ifndef __lvgl_H +#define __lvgl_H + +#include "stdlib.h" +#include "string.h" +#include +#include +#include + +/* Attribute to mark large constant arrays for example + * font's bitmaps */ +#define LV_ATTRIBUTE_LARGE_CONST + +/** Format of font character map. */ +enum { + LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY, + LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL, + LV_FONT_FMT_TXT_CMAP_SPARSE_TINY, + LV_FONT_FMT_TXT_CMAP_SPARSE_FULL, +}; + +typedef uint8_t lv_font_fmt_txt_cmap_type_t; + +typedef uint8_t lv_opa_t; + +/** This describes a glyph. */ +typedef struct { +#if LV_FONT_FMT_TXT_LARGE == 0 + uint32_t bitmap_index: 20; /**< Start index of the bitmap. A font can be max 1 MB. */ + uint32_t adv_w: 12; /**< Draw the next glyph after this width. 8.4 format (real_value * 16 is stored). */ + uint8_t box_w; /**< Width of the glyph's bounding box*/ + uint8_t box_h; /**< Height of the glyph's bounding box*/ + int8_t ofs_x; /**< x offset of the bounding box*/ + int8_t ofs_y; /**< y offset of the bounding box. Measured from the top of the line*/ +#else + uint32_t bitmap_index; /**< Start index of the bitmap. A font can be max 4 GB. */ + uint32_t adv_w; /**< Draw the next glyph after this width. 28.4 format (real_value * 16 is stored). */ + uint16_t box_w; /**< Width of the glyph's bounding box*/ + uint16_t box_h; /**< Height of the glyph's bounding box*/ + int16_t ofs_x; /**< x offset of the bounding box*/ + int16_t ofs_y; /**< y offset of the bounding box. Measured from the top of the line*/ +#endif +} lv_font_fmt_txt_glyph_dsc_t; + +/* 将码点映射到 `glyph_dsc` 的结构体 + * 支持多种格式以优化内存使用 + * 详见 https://github.com/lvgl/lv_font_conv/blob/master/doc/font_spec.md + */ +typedef struct { + /** 此范围的第一个Unicode字符 */ + uint32_t range_start; + + /** 与此范围相关的Unicode字符数。 + * 最后一个Unicode字符 = range_start + range_length - 1*/ + uint16_t range_length; + + /** 此范围的第一个字形ID(`glyph_dsc` 的数组索引) */ + uint16_t glyph_id_start; + + /* + 根据规范,有4种格式: + https://github.com/lvgl/lv_font_conv/blob/master/doc/font_spec.md + + 为简单起见引入“相对码点”: + rcp = codepoint - range_start + + 和一个搜索函数: + 在“数组”中搜索“值”并返回“值”的索引。 + + 格式0 紧凑 + unicode_list == NULL && glyph_id_ofs_list == NULL + glyph_id = glyph_id_start + rcp + + 格式0 完整 + unicode_list == NULL && glyph_id_ofs_list != NULL + glyph_id = glyph_id_start + glyph_id_ofs_list[rcp] + + Fomat 紧凑 + unicode_list != NULL && glyph_id_ofs_list == NULL + glyph_id = glyph_id_start + search(unicode_list, rcp) + + Fomat 完整 + unicode_list != NULL && glyph_id_ofs_list != NULL + glyph_id = glyph_id_start + glyph_id_ofs_list[search(unicode_list, rcp)] + */ + + const uint16_t *unicode_list; + + /** 如果(type == LV_FONT_FMT_TXT_CMAP_FORMAT0_...),则为 `uint8_t *` + * 如果(type == LV_FONT_FMT_TXT_CMAP_SPARSE_...),则为 `uint16_t *` + */ + const void *glyph_id_ofs_list; + + /** `unicode_list` 和/或 `glyph_id_ofs_list` 的长度 */ + uint16_t list_length; + + /** 此字符映射的类型 */ + lv_font_fmt_txt_cmap_type_t type; +} lv_font_fmt_txt_cmap_t; + +/* 描述字体的附加数据的结构体 */ +typedef struct { + /* 所有字形的位图 */ + const uint8_t *glyph_bitmap; + + /* 描述字形 */ + const lv_font_fmt_txt_glyph_dsc_t *glyph_dsc; + + /* 将字形映射到Unicode字符。 + * `lv_font_cmap_fmt_txt_t` 变量的数组 */ + const lv_font_fmt_txt_cmap_t *cmaps; + + /* 存储字距值。 + * 可以是 `lv_font_fmt_txt_kern_pair_t *` 或 `lv_font_kern_classes_fmt_txt_t *` + * 取决于 `kern_classes` */ + const void *kern_dsc; + + /* 缩放字距值,12.4格式 */ + uint16_t kern_scale; + + /* cmap表的数量 */ + uint16_t cmap_num: 10; + + /* 每像素位数:1, 2, 3, 4, 8 */ + uint16_t bpp: 4; + + /* `kern_dsc` 的类型 */ + uint16_t kern_classes: 1; + + /* + * 位图的存储格式 + * 取自 `lv_font_fmt_txt_bitmap_format_t` + */ + uint16_t bitmap_format: 2; + + /* 缓存最后一个字母和其字形ID */ + uint32_t last_letter; + uint32_t last_glyph_id; + +} lv_font_fmt_txt_dsc_t; + +/* 坐标类型。应为 `int16_t`(或在极端情况下为 `int32_t`) */ +typedef int16_t lv_coord_t; + +/** 描述字形属性的结构体 */ +typedef struct { + uint16_t adv_w; /**< 字形所需的空间。在此宽度后绘制下一个字形。8位整数,4位小数 */ + uint16_t box_w; /**< 字形的包围框宽度 */ + uint16_t box_h; /**< 字形的包围框高度 */ + int16_t ofs_x; /**< 包围框的x偏移量 */ + int16_t ofs_y; /**< 包围框的y偏移量 */ + uint8_t bpp; /**< 每像素位数:1, 2, 4, 8 */ +} lv_font_glyph_dsc_t; + +/** 描述字体的属性的结构体 */ +typedef struct _lv_font_struct { + /** 从字体中获取字形描述符 */ + bool (*get_glyph_dsc)(const struct _lv_font_struct *, lv_font_glyph_dsc_t *, uint32_t letter, uint32_t letter_next); + + /** 从字体中获取字形的位图 */ + const uint8_t *(*get_glyph_bitmap)(const struct _lv_font_struct *, uint32_t); + + /* 指向字体包中的字体的指针(必须具有相同的行高) */ + lv_coord_t line_height; /**< 任何文本都适合的实际行高 */ + lv_coord_t base_line; /**< 从行高顶部测量的基准线 */ + uint8_t subpx: 2; /**< `lv_font_subpx_t` 的一个元素 */ + + int8_t underline_position; /**< 下划线顶部与基准线之间的距离(< 0 表示在基准线下方)*/ + int8_t underline_thickness; /**< 下划线的厚度 */ + + void *dsc; /**< 在这里存储特定于实现或运行时数据或缓存 */ +} lv_font_t; + + +#define NULL 0 /* see */ + +/** Bitmap formats*/ +typedef enum { + LV_FONT_FMT_TXT_PLAIN = 0, + LV_FONT_FMT_TXT_COMPRESSED = 1, + LV_FONT_FMT_TXT_COMPRESSED_NO_PREFILTER = 1, +} lv_font_fmt_txt_bitmap_format_t; + + +typedef uintptr_t lv_uintptr_t; + + +/** 用于简单映射字距值对的结构体 */ +typedef struct { + /* 获取两个字符代码点之间的字距值: + 1. 从 `lv_font_fmt_txt_cmap_t` 中获取 `glyph_id_left` 和 `glyph_id_right` + 2. for(i = 0; i < pair_cnt * 2; i+2) + if(gylph_ids[i] == glyph_id_left && + gylph_ids[i+1] == glyph_id_right) + return values[i / 2]; + */ + const void *glyph_ids; /* 存储字形ID对 */ + const int8_t *values; /* 存储字距值 */ + uint32_t pair_cnt: 24; /* 字距值对的数量 */ + uint32_t glyph_ids_size: 2; /* 0: `glyph_ids` 存储为 `uint8_t`; 1: 存储为 `uint16_t` */ +} lv_font_fmt_txt_kern_pair_t; + +/** 更复杂但更优化的基于类别的字距值存储结构体 */ +typedef struct { + /* 结构体用于获取两个字符代码点之间的字距值: + 1. 从 `lv_font_fmt_txt_cmap_t` 中获取 `glyph_id_left` 和 `glyph_id_right` + 2. 获取左侧和右侧字形的类别,分别为 `left_class` 和 `right_class` + left_class = left_class_mapping[glyph_id_left]; + right_class = right_class_mapping[glyph_id_right]; + 3. value = class_pair_values[(left_class-1)*right_class_cnt + (right_class-1)] + */ + + const int8_t *class_pair_values; /* 存储左侧类别数 * 右侧类别数的值 */ + const uint8_t *left_class_mapping; /* 将字形ID映射到类别:索引 -> 字形ID -> 类别ID */ + const uint8_t *right_class_mapping; /* 将字形ID映射到类别:索引 -> 字形ID -> 类别ID */ + uint8_t left_class_cnt; /* 左侧类别数 */ + uint8_t right_class_cnt; /* 右侧类别数 */ +} lv_font_fmt_txt_kern_classes_t; + + +#endif \ No newline at end of file diff --git a/lib/inc/font/lvgl_font.h b/lib/inc/font/lvgl_font.h new file mode 100644 index 0000000..7bb7406 --- /dev/null +++ b/lib/inc/font/lvgl_font.h @@ -0,0 +1,20 @@ + +#pragma once + +#ifndef __lvgl_font_H +#define __lvgl_font_H + +#include "stdlib.h" +#include "string.h" +#include +#include +#include +#include "lvgl.h" + +#define LV_ANTIALIAS 0 //抗锯齿 + +char utf8_to_unicode(uint8_t *pInput, uint32_t *unicode_letter); + +char lv_draw_letter(const lv_font_t *font, uint32_t letter, int16_t pos_x, int16_t pos_y); + +#endif \ No newline at end of file diff --git a/lib/inc/oled/oled.h b/lib/inc/oled/oled.h index 9352cba..594ab14 100644 --- a/lib/inc/oled/oled.h +++ b/lib/inc/oled/oled.h @@ -1,3 +1,4 @@ +#pragma once #ifndef HW_LIB_OLED_H #define HW_LIB_OLED_H #ifdef __cplusplus @@ -219,11 +220,10 @@ void OLED_ShowString(OLED_T *dev, uint8_t x, uint8_t y, uint8_t *chr, uint8_t si * @param x: [输入] X坐标 * @param y: [输入] Y坐标 * @param str: [输入] 中文字符串指针 - * @param size: [输入] 字体大小 * @return void * @example OLED_ShowCHString(&oled_device, 0, 0, chinese_str, 16); **/ -void OLED_ShowCHString(OLED_T *dev, uint8_t x, uint8_t y, uint8_t *str, uint8_t size); +void OLED_ShowCHString(OLED_T *dev, uint8_t x, uint8_t y, uint8_t *str); /** * @brief 在OLED屏幕上显示数字 diff --git a/lib/inc/oled/oled_font_chuc.h b/lib/inc/oled/oled_font_chuc.h index 9d52911..a7e578b 100644 --- a/lib/inc/oled/oled_font_chuc.h +++ b/lib/inc/oled/oled_font_chuc.h @@ -1,46 +1,74 @@ +#pragma once #ifndef HW_LIB_OLED_FONT_CHUC_H #define HW_LIB_OLED_FONT_CHUC_H + + typedef struct { - uint8_t name[3]; + uint8_t unicode[2]; uint8_t data[32]; } Chinese_t; +uint8_t Hzk_size = 16; + Chinese_t Hzk[] = { { // Original: 字 {0x5B, 0x57}, - {0x10, 0x0C, 0x04, 0x24, 0x24, 0x24, 0x25, 0x26, 0xA4, 0x64, 0x24, 0x04, 0x04, 0x14, 0x0C, 0x00, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x42, 0x82, 0x7F, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00 - }}, + { + 0x10, 0x0C, 0x04, 0x24, 0x24, 0x24, 0x25, 0x26, 0xA4, 0x64, 0x24, 0x04, 0x04, 0x14, 0x0C, 0x00, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x42, 0x82, 0x7F, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, + } + }, { // Original: 库 {0x5E, 0x93}, - {0x00, 0x00, 0xFC, 0x24, 0x24, 0xA4, 0x64, 0x3D, 0xA6, 0x24, 0x24, 0x24, 0x24, 0x24, 0x04, 0x00, - 0x40, 0x30, 0x0F, 0x10, 0x13, 0x12, 0x12, 0x12, 0xFF, 0x12, 0x12, 0x12, 0x12, 0x10, 0x10, 0x00 - }}, + { + 0x00, 0x00, 0xFC, 0x24, 0x24, 0xA4, 0x64, 0x3D, 0xA6, 0x24, 0x24, 0x24, 0x24, 0x24, 0x04, 0x00, + 0x40, 0x30, 0x0F, 0x10, 0x13, 0x12, 0x12, 0x12, 0xFF, 0x12, 0x12, 0x12, 0x12, 0x10, 0x10, 0x00, + } + }, { // Original: 生 {0x75, 0x1F}, - {0x80, 0x40, 0x30, 0x1E, 0x10, 0x10, 0x10, 0xFF, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, - 0x40, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7F, 0x42, 0x42, 0x42, 0x42, 0x42, 0x40, 0x40, 0x00 - }}, + { + 0x80, 0x40, 0x30, 0x1E, 0x10, 0x10, 0x10, 0xFF, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, + 0x40, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7F, 0x42, 0x42, 0x42, 0x42, 0x42, 0x40, 0x40, 0x00, + } + }, { // Original: 成 {0x62, 0x10}, - {0x00, 0x00, 0xF8, 0x88, 0x88, 0x88, 0x88, 0x08, 0x08, 0xFF, 0x08, 0x09, 0x0A, 0xC8, 0x08, 0x00, - 0x80, 0x60, 0x1F, 0x00, 0x10, 0x20, 0x1F, 0x80, 0x40, 0x21, 0x16, 0x18, 0x26, 0x41, 0xF8, 0x00 - }}, + { + 0x00, 0x00, 0xF8, 0x88, 0x88, 0x88, 0x88, 0x08, 0x08, 0xFF, 0x08, 0x09, 0x0A, 0xC8, 0x08, 0x00, + 0x80, 0x60, 0x1F, 0x00, 0x10, 0x20, 0x1F, 0x80, 0x40, 0x21, 0x16, 0x18, 0x26, 0x41, 0xF8, 0x00, + } + }, { // Original: 测 {0x6D, 0x4B}, - {0x10, 0x60, 0x02, 0x8C, 0x00, 0xFE, 0x02, 0xF2, 0x02, 0xFE, 0x00, 0xF8, 0x00, 0xFF, 0x00, 0x00, - 0x04, 0x04, 0x7E, 0x01, 0x80, 0x47, 0x30, 0x0F, 0x10, 0x27, 0x00, 0x47, 0x80, 0x7F, 0x00, 0x00 - }}, + { + 0x10, 0x60, 0x02, 0x8C, 0x00, 0xFE, 0x02, 0xF2, 0x02, 0xFE, 0x00, 0xF8, 0x00, 0xFF, 0x00, 0x00, + 0x04, 0x04, 0x7E, 0x01, 0x80, 0x47, 0x30, 0x0F, 0x10, 0x27, 0x00, 0x47, 0x80, 0x7F, 0x00, 0x00, + } + }, { // Original: 试 {0x8B, 0xD5}, - {0x40, 0x40, 0x42, 0xCC, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0x10, 0x11, 0x16, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x3F, 0x10, 0x28, 0x60, 0x3F, 0x10, 0x10, 0x01, 0x0E, 0x30, 0x40, 0xF0, 0x00 - }}, + { + 0x40, 0x40, 0x42, 0xCC, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0x10, 0x11, 0x16, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x3F, 0x10, 0x28, 0x60, 0x3F, 0x10, 0x10, 0x01, 0x0E, 0x30, 0x40, 0xF0, 0x00, + } + }, }; + +Chinese_t *find_chinese_data(uint8_t unicode_high, uint8_t unicode_low) { + for (int i = 0; i < sizeof(Hzk) / sizeof(Chinese_t); ++i) { + if (Hzk[i].unicode[0] == unicode_high && Hzk[i].unicode[1] == unicode_low) { + return &Hzk[i]; + } + } + return NULL; +} + + #endif //HW_LIB_OLED_FONT_CHUC_H diff --git a/lib/src/font/lvgl_font.c b/lib/src/font/lvgl_font.c new file mode 100644 index 0000000..f2d9824 --- /dev/null +++ b/lib/src/font/lvgl_font.c @@ -0,0 +1,558 @@ + +#include "lvgl.h" +#include "lvgl_font.h" + +#define LV_HOR_RES_MAX 128 + +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// Show utf8_to_unicode +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +char utf8_to_unicode(uint8_t *pInput, uint32_t *unicode_letter) { + char outputSize = 0; + char ppOutput[8] = {0}; + + char *tmp = ppOutput; //临时变量,用于遍历输出字符串 + + if (*pInput > 0x00 && *pInput <= 0x7F) //处理单字节UTF8字符(英文字母、数字) + { + + *tmp = *pInput; + tmp++; + *tmp = 0; //小端法表示,在高地址填补0 + outputSize = 1; + } else if (((*pInput) & 0xE0) == 0xC0) //处理双字节UTF8字符 + { + + char high = *pInput; + + pInput++; + + char low = *pInput; + + if ((low & 0xC0) != 0x80) //检查是否为合法的UTF8字符表示 + return 0; //如果不是则报错 + + *tmp = (high << 6) + (low & 0x3F); + tmp++; + *tmp = (high >> 2) & 0x07; + outputSize = 2; + } else if (((*pInput) & 0xF0) == 0xE0) //处理三字节UTF8字符 + { + + char high = *pInput; + pInput++; + char middle = *pInput; + pInput++; + char low = *pInput; + + if (((middle & 0xC0) != 0x80) || ((low & 0xC0) != 0x80)) + return 0; + + *tmp = (middle << 6) + (low & 0x7F); + tmp++; + *tmp = (high << 4) + ((middle >> 2) & 0x0F); + outputSize = 3; + } else //对于其他字节数的UTF8字符不进行处理 + { + return 0; + } + unicode_letter[0] = ppOutput[1]; + unicode_letter[0] <<= 8; + unicode_letter[0] |= ppOutput[0]; + + return outputSize; +} + +void +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// Show draw_letter_normal +// lv_opa_t opa ; //透明度 0-255 +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void draw_letter_normal(uint16_t pos_x, uint16_t pos_y, lv_font_glyph_dsc_t *g, const uint8_t *map_p) { + uint32_t bitmask_init; + uint32_t bitmask; + uint32_t bpp = g->bpp; + + if (bpp == 3) + bpp = 4; + + switch (bpp) { + case 1: + bitmask_init = 0x80; + break; + case 2: + bitmask_init = 0xC0; + break; + case 4: + bitmask_init = 0xF0; + break; + case 8: + bitmask_init = 0xFF; + break; + default: + return; /*Invalid bpp. Can't render the letter*/ + } + + int32_t col, row; + int32_t box_w = g->box_w; + int32_t box_h = g->box_h; + int32_t width_bit = box_w * bpp; /*Letter width in bits*/ + + /* Calculate the col/row start/end on the map*/ + int32_t col_start = 0; + int32_t col_end = box_w; + int32_t row_start = 0; + int32_t row_end = box_h; + + /*Move on the map too*/ + uint32_t bit_ofs = (row_start * width_bit) + (col_start * bpp); + map_p += bit_ofs >> 3; + + uint8_t letter_px; + uint32_t col_bit; + col_bit = bit_ofs & 0x7; /* "& 0x7" equals to "% 8" just faster */ + + // uint32_t mask_buf_size = box_w * box_h > LV_HOR_RES_MAX ? LV_HOR_RES_MAX : box_w * box_h; +// unsigned short mask_buf[LV_HOR_RES_MAX / 2]; + uint8_t fbuf[128 / 8]; + int32_t mask_p = 0; + + uint32_t col_bit_max = 8 - bpp; + uint32_t col_bit_row_ofs = (box_w + col_start - col_end) * bpp; + uint8_t index = 0; + for (row = row_start; row < row_end; row++) { + int32_t mask_p_start = mask_p; + + bitmask = bitmask_init >> col_bit; + for (col = col_start; col < col_end; col++) { + /*Load the pixel's opacity into the mask*/ + letter_px = (*map_p & bitmask) >> (col_bit_max - col_bit); + +// if (letter_px) 1; +// else 0; + + if (letter_px) { + fbuf[index / 8] |= (1 << (index % 8)); + } else { + fbuf[index / 8] &= ~(1 << (index % 8)); + } + index++; + + /*Go to the next column*/ + if (col_bit < col_bit_max) { + col_bit += bpp; + bitmask >>= bpp; + } else { + col_bit = 0; + bitmask = bitmask_init; + map_p++; + } + + /*Next mask byte*/ + mask_p++; + } + + { + + // LCD_fill_fast(pos_x, pos_y, box_w, 1, (uint8_t *)mask_buf, box_w); + + //// 读取fbuf的当前位置的二进制位 + //bool read_bit = (fbuf[pos / 8] >> (pos % 8)) & 1; + + pos_y++; + mask_p = 0; + } //避免大字体,大缓存 ,单行即刻刷入 + + col_bit += col_bit_row_ofs; + map_p += (col_bit >> 3); + col_bit = col_bit & 0x7; + } + // LCD_fill_fast(pos_x, pos_y, box_w, box_h, (uint8_t *)mask_buf, box_w * box_h); // 如果 大缓存 一次刷入 +} + +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// Show lv_draw_letter +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +char lv_draw_letter(const lv_font_t *font, uint32_t letter, int16_t pos_x, int16_t pos_y) { + const uint8_t *map_p; + + lv_font_glyph_dsc_t g; + if (!font->get_glyph_dsc(font, &g, letter, '\0')) //获取字体 信息 + return 0; + + if (letter == ' ') // lvgl 空格没有实际数据 直接跳过 + return g.box_w; + + /* Don't draw anything if the character is empty. E.g. space */ + if ((g.box_h == 0) || (g.box_w == 0)) + return 0; + + map_p = font->get_glyph_bitmap(font, letter); //获取字体 实际数据 + if (map_p == NULL) + return 0; + + pos_x += g.ofs_x; //偏移修正 + pos_y += font->line_height - g.box_h - g.ofs_y; + + if (pos_x < 1) //有的时候会为负 + pos_x = 1; + if (pos_y < 1) //有的时候会为负 + pos_y = 1; + + draw_letter_normal(pos_x, pos_y, &g, map_p); + + return g.box_w; +} + +#if LV_FONT_UNSCII_8 + +/** Searches base[0] to base[n - 1] for an item that matches *key. + * + * @note The function cmp must return negative if its first + * argument (the search key) is less that its second (a table entry), + * zero if equal, and positive if greater. + * + * @note Items in the array must be in ascending order. + * + * @param key Pointer to item being searched for + * @param base Pointer to first element to search + * @param n Number of elements + * @param size Size of each element + * @param cmp Pointer to comparison function (see #lv_font_codeCompare as a comparison function + * example) + * + * @return a pointer to a matching item, or NULL if none exists. + */ +void *_lv_utils_bsearch(const void *key, const void *base, uint32_t n, uint32_t size, + int32_t (*cmp)(const void *pRef, const void *pElement)) +{ + const char *middle; + int32_t c; + + for (middle = base; n != 0;) + { + middle += (n / 2) * size; + if ((c = (*cmp)(key, middle)) > 0) + { + n = (n / 2) - ((n & 1) == 0); + base = (middle += size); + } + else if (c < 0) + { + n /= 2; + middle = base; + } + else + { + return (char *)middle; + } + } + return NULL; +} +/** Code Comparator. + * + * Compares the value of both input arguments. + * + * @param[in] pRef Pointer to the reference. + * @param[in] pElement Pointer to the element to compare. + * + * @return Result of comparison. + * @retval < 0 Reference is greater than element. + * @retval = 0 Reference is equal to element. + * @retval > 0 Reference is less than element. + * + */ +static int32_t unicode_list_compare(const void *ref, const void *element) +{ + return ((int32_t)(*(uint16_t *)ref)) - ((int32_t)(*(uint16_t *)element)); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static uint32_t get_glyph_dsc_id(const lv_font_t *font, uint32_t letter) +{ + if (letter == '\0') + return 0; + + lv_font_fmt_txt_dsc_t *fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc; + + /*Check the cache first*/ + if (letter == fdsc->last_letter) + return fdsc->last_glyph_id; + + uint16_t i; + for (i = 0; i < fdsc->cmap_num; i++) + { + + /*Relative code point*/ + uint32_t rcp = letter - fdsc->cmaps[i].range_start; + if (rcp > fdsc->cmaps[i].range_length) + continue; + uint32_t glyph_id = 0; + if (fdsc->cmaps[i].type == LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY) + { + glyph_id = fdsc->cmaps[i].glyph_id_start + rcp; + } + else if (fdsc->cmaps[i].type == LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL) + { + const uint8_t *gid_ofs_8 = fdsc->cmaps[i].glyph_id_ofs_list; + glyph_id = fdsc->cmaps[i].glyph_id_start + gid_ofs_8[rcp]; + } + else if (fdsc->cmaps[i].type == LV_FONT_FMT_TXT_CMAP_SPARSE_TINY) + { + uint16_t key = rcp; + uint8_t *p = _lv_utils_bsearch(&key, fdsc->cmaps[i].unicode_list, fdsc->cmaps[i].list_length, + sizeof(fdsc->cmaps[i].unicode_list[0]), unicode_list_compare); + + if (p) + { + lv_uintptr_t ofs = (lv_uintptr_t)(p - (uint8_t *)fdsc->cmaps[i].unicode_list); + ofs = ofs >> 1; /*The list stores `uint16_t` so the get the index divide by 2*/ + glyph_id = fdsc->cmaps[i].glyph_id_start + ofs; + } + } + else if (fdsc->cmaps[i].type == LV_FONT_FMT_TXT_CMAP_SPARSE_FULL) + { + uint16_t key = rcp; + uint8_t *p = _lv_utils_bsearch(&key, fdsc->cmaps[i].unicode_list, fdsc->cmaps[i].list_length, + sizeof(fdsc->cmaps[i].unicode_list[0]), unicode_list_compare); + + if (p) + { + lv_uintptr_t ofs = (lv_uintptr_t)(p - (uint8_t *)fdsc->cmaps[i].unicode_list); + ofs = ofs >> 1; /*The list stores `uint16_t` so the get the index divide by 2*/ + const uint8_t *gid_ofs_16 = fdsc->cmaps[i].glyph_id_ofs_list; + glyph_id = fdsc->cmaps[i].glyph_id_start + gid_ofs_16[ofs]; + } + } + + /*Update the cache*/ + fdsc->last_letter = letter; + fdsc->last_glyph_id = glyph_id; + return glyph_id; + } + + fdsc->last_letter = letter; + fdsc->last_glyph_id = 0; + return 0; +} +/** + * Used as `get_glyph_bitmap` callback in LittelvGL's native font format if the font is uncompressed. + * @param font pointer to font + * @param unicode_letter an unicode letter which bitmap should be get + * @return pointer to the bitmap or NULL if not found + */ +const uint8_t *lv_font_get_bitmap_fmt_txt(const lv_font_t *font, uint32_t unicode_letter) +{ + if (unicode_letter == '\t') + unicode_letter = ' '; + + lv_font_fmt_txt_dsc_t *fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc; + uint32_t gid = get_glyph_dsc_id(font, unicode_letter); + if (!gid) + return NULL; + + const lv_font_fmt_txt_glyph_dsc_t *gdsc = &fdsc->glyph_dsc[gid]; + + if (fdsc->bitmap_format == LV_FONT_FMT_TXT_PLAIN) + { + if (gdsc) + return &fdsc->glyph_bitmap[gdsc->bitmap_index]; + } + /*Handle compressed bitmap*/ + else + { +#if LV_USE_FONT_COMPRESSED + uint32_t gsize = gdsc->box_w * gdsc->box_h; + if (gsize == 0) + return NULL; + + uint32_t buf_size = gsize; + /*Compute memory size needed to hold decompressed glyph, rounding up*/ + switch (fdsc->bpp) + { + case 1: + buf_size = (gsize + 7) >> 3; + break; + case 2: + buf_size = (gsize + 3) >> 2; + break; + case 3: + buf_size = (gsize + 1) >> 1; + break; + case 4: + buf_size = (gsize + 1) >> 1; + break; + } + + if (_lv_mem_get_size(LV_GC_ROOT(_lv_font_decompr_buf)) < buf_size) + { + LV_GC_ROOT(_lv_font_decompr_buf) = lv_mem_realloc(LV_GC_ROOT(_lv_font_decompr_buf), buf_size); + LV_ASSERT_MEM(LV_GC_ROOT(_lv_font_decompr_buf)); + if (LV_GC_ROOT(_lv_font_decompr_buf) == NULL) + return NULL; + } + + bool prefilter = fdsc->bitmap_format == LV_FONT_FMT_TXT_COMPRESSED ? true : false; + decompress(&fdsc->glyph_bitmap[gdsc->bitmap_index], LV_GC_ROOT(_lv_font_decompr_buf), gdsc->box_w, gdsc->box_h, + (uint8_t)fdsc->bpp, + prefilter); + return LV_GC_ROOT(_lv_font_decompr_buf); +#else /* !LV_USE_FONT_COMPRESSED */ + return NULL; +#endif + } + + /*If not returned earlier then the letter is not found in this font*/ + return NULL; +} + +static int32_t kern_pair_8_compare(const void *ref, const void *element) +{ + const uint8_t *ref8_p = ref; + const uint8_t *element8_p = element; + + /*If the MSB is different it will matter. If not return the diff. of the LSB*/ + if (ref8_p[0] != element8_p[0]) + return (int32_t)ref8_p[0] - element8_p[0]; + else + return (int32_t)ref8_p[1] - element8_p[1]; +} + +static int32_t kern_pair_16_compare(const void *ref, const void *element) +{ + const uint16_t *ref16_p = ref; + const uint16_t *element16_p = element; + + /*If the MSB is different it will matter. If not return the diff. of the LSB*/ + if (ref16_p[0] != element16_p[0]) + return (int32_t)ref16_p[0] - element16_p[0]; + else + return (int32_t)ref16_p[1] - element16_p[1]; +} +static int8_t get_kern_value(const lv_font_t *font, uint32_t gid_left, uint32_t gid_right) +{ + lv_font_fmt_txt_dsc_t *fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc; + + int8_t value = 0; + + if (fdsc->kern_classes == 0) + { + /*Kern pairs*/ + const lv_font_fmt_txt_kern_pair_t *kdsc = fdsc->kern_dsc; + if (kdsc->glyph_ids_size == 0) + { + /* Use binary search to find the kern value. + * The pairs are ordered left_id first, then right_id secondly. */ + const uint8_t *g_ids = kdsc->glyph_ids; + uint16_t g_id_both = (gid_right << 8) + gid_left; /*Create one number from the ids*/ + uint8_t *kid_p = _lv_utils_bsearch(&g_id_both, g_ids, kdsc->pair_cnt, 2, kern_pair_8_compare); + + /*If the `g_id_both` were found get its index from the pointer*/ + if (kid_p) + { + lv_uintptr_t ofs = (lv_uintptr_t)(kid_p - g_ids); + ofs = ofs >> 1; /*ofs is for pair, divide by 2 to refer as a single value*/ + value = kdsc->values[ofs]; + } + } + else if (kdsc->glyph_ids_size == 1) + { + /* Use binary search to find the kern value. + * The pairs are ordered left_id first, then right_id secondly. */ + const uint16_t *g_ids = kdsc->glyph_ids; + lv_uintptr_t g_id_both = (uint32_t)((uint32_t)gid_right << 8) + gid_left; /*Create one number from the ids*/ + uint8_t *kid_p = _lv_utils_bsearch(&g_id_both, g_ids, kdsc->pair_cnt, 4, kern_pair_16_compare); + + /*If the `g_id_both` were found get its index from the pointer*/ + if (kid_p) + { + lv_uintptr_t ofs = (lv_uintptr_t)(kid_p - (const uint8_t *)g_ids); + ofs = ofs >> 4; /*ofs is 4 byte pairs, divide by 4 to refer as a single value*/ + value = kdsc->values[ofs]; + } + } + else + { + /*Invalid value*/ + } + } + else + { + /*Kern classes*/ + const lv_font_fmt_txt_kern_classes_t *kdsc = fdsc->kern_dsc; + uint8_t left_class = kdsc->left_class_mapping[gid_left]; + uint8_t right_class = kdsc->right_class_mapping[gid_right]; + + /* If class = 0, kerning not exist for that glyph + * else got the value form `class_pair_values` 2D array*/ + if (left_class > 0 && right_class > 0) + { + value = kdsc->class_pair_values[(left_class - 1) * kdsc->right_class_cnt + (right_class - 1)]; + } + } + return value; +} + +/** + * Used as `get_glyph_dsc` callback in LittelvGL's native font format if the font is uncompressed. + * @param font_p pointer to font + * @param dsc_out store the result descriptor here + * @param letter an UNICODE letter code + * @return true: descriptor is successfully loaded into `dsc_out`. + * false: the letter was not found, no data is loaded to `dsc_out` + */ +bool lv_font_get_glyph_dsc_fmt_txt(const lv_font_t *font, lv_font_glyph_dsc_t *dsc_out, uint32_t unicode_letter, + uint32_t unicode_letter_next) +{ + bool is_tab = false; + if (unicode_letter == '\t') + { + unicode_letter = ' '; + is_tab = true; + } + lv_font_fmt_txt_dsc_t *fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc; + uint32_t gid = get_glyph_dsc_id(font, unicode_letter); + if (!gid) + return false; + + int8_t kvalue = 0; + if (fdsc->kern_dsc) + { + uint32_t gid_next = get_glyph_dsc_id(font, unicode_letter_next); + if (gid_next) + { + kvalue = get_kern_value(font, gid, gid_next); + } + } + + /*Put together a glyph dsc*/ + const lv_font_fmt_txt_glyph_dsc_t *gdsc = &fdsc->glyph_dsc[gid]; + + int32_t kv = ((int32_t)((int32_t)kvalue * fdsc->kern_scale) >> 4); + + uint32_t adv_w = gdsc->adv_w; + if (is_tab) + adv_w *= 2; + + adv_w += kv; + adv_w = (adv_w + (1 << 3)) >> 4; + + dsc_out->adv_w = adv_w; + dsc_out->box_h = gdsc->box_h; + dsc_out->box_w = gdsc->box_w; + dsc_out->ofs_x = gdsc->ofs_x; + dsc_out->ofs_y = gdsc->ofs_y; + dsc_out->bpp = (uint8_t)fdsc->bpp; + + if (is_tab) + dsc_out->box_w = dsc_out->box_w * 2; + + return true; +} + +#endif diff --git a/lib/src/oled/oled.cpp b/lib/src/oled/oled.cpp index aa39040..ccf1e1c 100644 --- a/lib/src/oled/oled.cpp +++ b/lib/src/oled/oled.cpp @@ -1,6 +1,7 @@ #include "oled.h" #include "oled_font.h" #include "oled_font_chuc.h" +#include "../../inc/font/lvgl_font.h" #define BUFPOINT(x, y) (*(dev->buf + x + (y * dev->width))) #define GET_BIT(x, bit) ((x & (1 << bit)) >> bit) @@ -220,20 +221,24 @@ void OLED_ShowString(OLED_T *dev, uint8_t x, uint8_t y, uint8_t *chr, uint8_t si } } -void OLED_ShowCHString(OLED_T *dev, uint8_t x, uint8_t y, uint8_t *str, uint8_t size) { - uint8_t i, temp, size2, ys = y, xs = x; +void OLED_ShowCHString(OLED_T *dev, uint8_t x, uint8_t y, uint8_t *str) { + uint8_t i, temp, size2, ys = y, xs = x, c = x; uint16_t index; - size2 = (size / 8 + ((size % 8) ? 1 : 0)) * (size); //得到字体一个字符对应点阵集所占的字节数 + size2 = Hzk_size * Hzk_size / 8; //得到字体一个字符对应点阵集所占的字节数 while (*str) { - for (index = 0; index < sizeof(Hzk) / 35; index++) { - if (Hzk[index].name[0] == str[0] && Hzk[index].name[1] == str[1])//对比汉字区码位码 + for (index = 0; index < sizeof(Hzk) / sizeof(Chinese_t); index++) { + if (Hzk[index].unicode[0] == str[0] && Hzk[index].unicode[1] == str[1])//对比汉字区码位码 { for (i = 0; i < size2; i++) { temp = Hzk[index].data[i]; - OLED_BSet(dev, xs + (i % size), ys, temp); - if (i == size2 / 2)ys += size / 2; + OLED_BSet(dev, xs, ys, temp); + xs++; + if (xs - c == Hzk_size) { + ys += 8; + xs = c; + c += Hzk_size; + } } - xs += size; ys = y; str++; str++; @@ -243,6 +248,21 @@ void OLED_ShowCHString(OLED_T *dev, uint8_t x, uint8_t y, uint8_t *str, uint8_t } } } +// +//void OLED_DisplayString(OLED_T *dev, const lv_font_t *font, uint8_t *s, uint16_t x, uint16_t y) { +// uint32_t unicode_letter; +// while (*s) { +// if (font) { +// s += utf8_to_unicode(s, &unicode_letter); +// x += lv_draw_letter(font, unicode_letter, x, y); +// x += 2; //字间距 +// } else { +// OLED_ShowChar(dev, x, y, *s, 6); +// x += 6; +// s++; +// } +// } +//} uint32_t OLED_Pow(uint8_t m, uint8_t n) { uint32_t result = 1; diff --git a/tools/unicode_ch_gen.py b/tools/unicode_ch_gen.py index 6447a64..e82e8d4 100644 --- a/tools/unicode_ch_gen.py +++ b/tools/unicode_ch_gen.py @@ -18,6 +18,50 @@ def generate_chinese_struct(char_code, font, size): return result +# from bitarray import bitarray +# # from PIL import Image, ImageDraw +# # from bitarray import bitarray + + +# def generate_chinese_struct(char_code, font, size): +# height=(size[1] + 7) // 8 * 8 +# wight=(size[0] + 7) // 8 * 8 +# image = Image.new('1', size, 1) +# draw = ImageDraw.Draw(image) +# draw.text((0, -1), char_code, font=font, fill=0) +# image.show() +# bitmap = bitarray() +# for w in range(size[1]): +# for h in range(size[0]): +# # if h > size[1] or w > size[0]: +# # bitmap.append(False) +# # else: +# if image.getpixel((w, h)) == 0: +# bitmap.append(True) +# print('■', end=' ') +# else: +# bitmap.append(False) +# print('0', end=' ') +# print() +# result = np.zeros(size[0] * size[1] // 8, dtype=np.uint8) +# # for i in range(height): +# # for j in range(wight // 8): +# # for k in range(8): +# # if bitmap[j * 8 + k, i]==1: +# # result[j * height + i] |= (1 << k) +# for h in range(height): +# for w in range(wight): +# if bitmap[w+h]: +# #前景字符(即用来表示汉字笔画的输出字符) +# print('■', end=' ') +# else: +# +# # 背景字符(即用来表示背景的输出字符) +# print('0', end=' ') +# print() +# return result + + def generate_chinese_array(input_str, font_str, size): font = pilfont.truetype(font_str, size=size[1]) chinese_array = [] @@ -64,7 +108,7 @@ def generate_and_write_chinese_array_output(): # simsun: 宋体 # kaiti: 楷体 # size = (20, 20) - # size = (20, 20) + # size = (12, 12) size = (16, 16) chinese_array = generate_chinese_array("字库生成测试", 'simsun', size)