Browse Source

add: 0.01版本 新增FreeType字体库支持,提供字体初始化、反初始化、字符串宽度获取及绘制功能

zengeshuai 5 tháng trước cách đây
mục cha
commit
472d1f4e03

+ 155 - 0
components/freetypefont/binding/luat_lib_freetypefont.c

@@ -0,0 +1,155 @@
+/*
+@module  freetypefont
+@summary FreeType字体库
+@version 1.0
+@date    2025.10.16
+@tag LUAT_USE_FREETYPEFONT
+@usage
+-- 使用FreeType渲染TTF字体
+-- 需要准备TTF字体文件
+
+-- 初始化字体
+freetypefont.init("/sd/font.ttf")
+
+-- 获取字符串宽度
+local width = freetypefont.getStrWidth("Hello世界", 24)
+print("字符串宽度:", width)
+
+-- 绘制文本
+lcd.drawfreefontUtf8(10, 50, "Hello世界", 24, 0xFFFFFF)
+*/
+
+#include "luat_base.h"
+#include "luat_freetypefont.h"
+#include "luat_lcd.h"
+
+#define LUAT_LOG_TAG "freetypefont"
+#include "luat_log.h"
+#include "rotable2.h"
+
+#include "luat_conf_bsp.h"
+
+/**
+初始化FreeType字体库
+@api freetypefont.init(ttf_path)
+@string ttf_path TTF字体文件路径
+@return boolean 成功返回true,失败返回false
+@usage
+freetypefont.init("/sd/font.ttf")
+*/
+static int l_freetypefont_init(lua_State* L) {
+    size_t len = 0;
+    const char* ttf_path = luaL_checklstring(L, 1, &len);
+    
+    if (!ttf_path || len == 0) {
+        LLOGE("TTF path is empty");
+        lua_pushboolean(L, 0);
+        return 1;
+    }
+    
+    int result = luat_freetypefont_init(ttf_path);
+    lua_pushboolean(L, result);
+    return 1;
+}
+
+/**
+反初始化FreeType字体库
+@api freetypefont.deinit()
+@usage
+freetypefont.deinit()
+*/
+static int l_freetypefont_deinit(lua_State* L) {
+    (void)L;
+    luat_freetypefont_deinit();
+    return 0;
+}
+
+/**
+获取当前初始化状态
+@api freetypefont.state()
+@return int 状态值:0-未初始化,1-已初始化,2-错误
+@usage
+local state = freetypefont.state()
+if state == 1 then
+    print("FreeType已初始化")
+end
+*/
+static int l_freetypefont_state(lua_State* L) {
+    luat_freetypefont_state_t state = luat_freetypefont_get_state();
+    lua_pushinteger(L, (int)state);
+    return 1;
+}
+
+/**
+获取UTF-8字符串宽度
+@api freetypefont.getStrWidth(str, fontSize)
+@string str UTF-8字符串
+@int fontSize 字体大小(像素)
+@return int 字符串宽度(像素)
+@usage
+local width = freetypefont.getStrWidth("Hello世界", 24)
+print("字符串宽度:", width)
+*/
+static int l_freetypefont_get_str_width(lua_State* L) {
+    size_t len = 0;
+    const char* str = luaL_checklstring(L, 1, &len);
+    int fontSize = luaL_checkinteger(L, 2);
+    
+    if (fontSize <= 0 || fontSize > 255) {
+        LLOGE("Invalid font size: %d", fontSize);
+        lua_pushinteger(L, 0);
+        return 1;
+    }
+    
+    unsigned int width = luat_freetypefont_get_str_width(str, (unsigned char)fontSize);
+    lua_pushinteger(L, width);
+    return 1;
+}
+
+/**
+绘制UTF-8字符串到LCD
+@api freetypefont.drawUtf8(x, y, str, fontSize, color)
+@int x X坐标
+@int y Y坐标(左下角为基准)
+@string str UTF-8字符串
+@int fontSize 字体大小(像素)
+@int color 颜色值(RGB565格式)
+@return boolean 成功返回true,失败返回false
+@usage
+-- 绘制白色文本
+freetypefont.drawUtf8(10, 50, "Hello世界", 24, 0xFFFF)
+-- 绘制红色文本
+freetypefont.drawUtf8(10, 80, "红色文本", 24, 0xF800)
+*/
+static int l_freetypefont_draw_utf8(lua_State* L) {
+    int x = luaL_checkinteger(L, 1);
+    int y = luaL_checkinteger(L, 2);
+    size_t len = 0;
+    const char* str = luaL_checklstring(L, 3, &len);
+    int fontSize = luaL_checkinteger(L, 4);
+    uint32_t color = luaL_checkinteger(L, 5);
+    
+    if (fontSize <= 0 || fontSize > 255) {
+        LLOGE("Invalid font size: %d", fontSize);
+        lua_pushboolean(L, 0);
+        return 1;
+    }
+    
+    int result = luat_freetypefont_draw_utf8(x, y, str, (unsigned char)fontSize, color);
+    lua_pushboolean(L, result == 0 ? 1 : 0);
+    return 1;
+}
+
+static const rotable_Reg_t reg_freetypefont[] = {
+    { "init",        ROREG_FUNC(l_freetypefont_init)},
+    { "deinit",      ROREG_FUNC(l_freetypefont_deinit)},
+    { "state",       ROREG_FUNC(l_freetypefont_state)},
+    { "getStrWidth", ROREG_FUNC(l_freetypefont_get_str_width)},
+    { "drawUtf8",    ROREG_FUNC(l_freetypefont_draw_utf8)},
+    { NULL,          ROREG_INT(0)}
+};
+
+LUAMOD_API int luaopen_freetypefont(lua_State *L) {
+    luat_newlib2(L, reg_freetypefont);
+    return 1;
+}

+ 109 - 0
components/freetypefont/inc/luat_freetypefont.h

@@ -0,0 +1,109 @@
+#ifndef _LUAT_FREETYPEFONT_H_
+#define _LUAT_FREETYPEFONT_H_
+
+#include "luat_base.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// FreeType 库初始化状态
+typedef enum {
+    LUAT_FREETYPEFONT_STATE_UNINIT = 0,
+    LUAT_FREETYPEFONT_STATE_INITED = 1,
+    LUAT_FREETYPEFONT_STATE_ERROR = 2
+} luat_freetypefont_state_t;
+
+/**
+ * @brief 初始化 FreeType 字体库
+ * 
+ * @param ttf_path TTF字体文件路径
+ * @return int 0: 失败, 1: 成功
+ */
+int luat_freetypefont_init(const char* ttf_path);
+
+/**
+ * @brief 反初始化 FreeType 字体库
+ */
+void luat_freetypefont_deinit(void);
+
+/**
+ * @brief 获取当前初始化状态
+ * 
+ * @return luat_freetypefont_state_t 当前状态
+ */
+luat_freetypefont_state_t luat_freetypefont_get_state(void);
+
+/**
+ * @brief 获取字符位图(1bpp单色)
+ * 
+ * @param pBits 输出缓冲区
+ * @param sty 样式(保留,暂未使用)
+ * @param fontCode Unicode字符码
+ * @param width 字符宽度(像素)
+ * @param height 字符高度(像素)
+ * @param thick 粗细(保留,暂未使用)
+ * @return unsigned int 实际字符宽度
+ */
+unsigned int luat_freetypefont_get_char(
+    unsigned char *pBits,
+    unsigned char sty,
+    unsigned long fontCode,
+    unsigned char width,
+    unsigned char height,
+    unsigned char thick
+);
+
+/**
+ * @brief 获取字符位图(灰度)
+ * 
+ * @param pBits 输出缓冲区
+ * @param sty 样式(保留,暂未使用)
+ * @param fontCode Unicode字符码
+ * @param fontSize 字体大小(像素)
+ * @param thick 粗细(保留,暂未使用)
+ * @return unsigned int* 返回数组:[0]=实际宽度, [1]=灰度阶数
+ */
+unsigned int* luat_freetypefont_get_char_gray(
+    unsigned char *pBits,
+    unsigned char sty,
+    unsigned long fontCode,
+    unsigned char fontSize,
+    unsigned char thick
+);
+
+/**
+ * @brief 获取UTF-8字符串宽度
+ * 
+ * @param str UTF-8字符串
+ * @param fontSize 字体大小
+ * @return unsigned int 字符串总宽度(像素)
+ */
+unsigned int luat_freetypefont_get_str_width(
+    const char* str,
+    unsigned char fontSize
+);
+
+/**
+ * @brief 绘制UTF-8字符串到LCD
+ * 
+ * @param x X坐标
+ * @param y Y坐标
+ * @param str UTF-8字符串
+ * @param fontSize 字体大小
+ * @param color 颜色值
+ * @return int 0: 成功, -1: 失败
+ */
+int luat_freetypefont_draw_utf8(
+    int x,
+    int y,
+    const char* str,
+    unsigned char fontSize,
+    uint32_t color
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LUAT_FREETYPEFONT_H_ */

+ 393 - 0
components/freetypefont/src/luat_freetypefont.c

@@ -0,0 +1,393 @@
+#include "luat_freetypefont.h"
+#include "luat_lcd.h"
+#include "luat_fs.h"
+#include "luat_mem.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LUAT_LOG_TAG "freetype"
+#include "luat_log.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+
+// 全局 FreeType 库和字体面
+static FT_Library g_ft_lib = NULL;
+static FT_Face g_ft_face = NULL;
+static luat_freetypefont_state_t g_ft_state = LUAT_FREETYPEFONT_STATE_UNINIT;
+static char* g_font_path = NULL;
+
+// 返回值缓存(用于 get_char_gray)
+static unsigned int g_gray_result[2] = {0, 0};
+
+// 内联辅助函数
+static inline uint32_t min_u32(uint32_t a, uint32_t b) { return a < b ? a : b; }
+static inline uint32_t max_u32(uint32_t a, uint32_t b) { return a > b ? a : b; }
+
+// 设置 1bpp 位图的位
+static inline void set_bit_1bpp(uint8_t* buf, uint32_t w, uint32_t x, uint32_t y) {
+    uint32_t bytes_per_row = (w + 7) / 8;
+    uint32_t byte_index = y * bytes_per_row + (x / 8);
+    uint8_t bit_pos = 7 - (x % 8);
+    buf[byte_index] |= (uint8_t)(1u << bit_pos);
+}
+
+// 设置灰度像素(2bpp/4bpp)
+static inline void set_pix_gray(uint8_t* buf, uint32_t w, uint32_t x, uint32_t y, uint8_t bpp, uint8_t val) {
+    if (bpp == 4) {
+        uint32_t bytes_per_row = ((w + 7) / 8) * 4;
+        uint32_t byte_index = y * bytes_per_row + (x / 2);
+        uint8_t shift = (uint8_t)((1 - (x % 2)) * 4);
+        uint8_t mask = (uint8_t)(0x0Fu << shift);
+        buf[byte_index] = (uint8_t)((buf[byte_index] & ~mask) | ((uint8_t)(val & 0x0F) << shift));
+    } else if (bpp == 2) {
+        uint32_t bytes_per_row = ((w + 7) / 8) * 2;
+        uint32_t byte_index = y * bytes_per_row + (x / 4);
+        uint8_t shift = (uint8_t)((3 - (x % 4)) * 2);
+        uint8_t mask = (uint8_t)(0x03u << shift);
+        buf[byte_index] = (uint8_t)((buf[byte_index] & ~mask) | ((uint8_t)(val & 0x03) << shift));
+    }
+}
+
+// UTF-8 解码
+static uint32_t utf8_next_char(const char** str) {
+    const uint8_t* s = (const uint8_t*)*str;
+    uint32_t code = 0;
+    
+    if (s[0] == 0) {
+        return 0xFFFFFFFF;
+    }
+    
+    if (s[0] < 0x80) {
+        code = s[0];
+        *str += 1;
+    } else if ((s[0] & 0xE0) == 0xC0) {
+        code = ((s[0] & 0x1F) << 6) | (s[1] & 0x3F);
+        *str += 2;
+    } else if ((s[0] & 0xF0) == 0xE0) {
+        code = ((s[0] & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
+        *str += 3;
+    } else if ((s[0] & 0xF8) == 0xF0) {
+        code = ((s[0] & 0x07) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
+        *str += 4;
+    } else {
+        *str += 1;
+        return 0xFFFD; // 替换字符
+    }
+    
+    return code;
+}
+
+int luat_freetypefont_init(const char* ttf_path) {
+    if (g_ft_state == LUAT_FREETYPEFONT_STATE_INITED) {
+        LLOGD("FreeType already initialized, deinit first");
+        luat_freetypefont_deinit();
+    }
+    
+    if (!ttf_path) {
+        LLOGE("TTF path is NULL");
+        g_ft_state = LUAT_FREETYPEFONT_STATE_ERROR;
+        return 0;
+    }
+    
+    // 检查文件是否存在 - 使用LuatOS文件系统接口
+    FILE* fp = luat_fs_fopen(ttf_path, "rb");
+    if (!fp) {
+        LLOGE("Cannot open TTF file: %s", ttf_path);
+        g_ft_state = LUAT_FREETYPEFONT_STATE_ERROR;
+        return 0;
+    }
+    luat_fs_fclose(fp);
+    
+    // 初始化 FreeType 库
+    if (FT_Init_FreeType(&g_ft_lib)) {
+        LLOGE("Failed to initialize FreeType library");
+        g_ft_state = LUAT_FREETYPEFONT_STATE_ERROR;
+        return 0;
+    }
+    
+    // 加载字体面
+    if (FT_New_Face(g_ft_lib, ttf_path, 0, &g_ft_face)) {
+        LLOGE("Failed to load font face: %s", ttf_path);
+        FT_Done_FreeType(g_ft_lib);
+        g_ft_lib = NULL;
+        g_ft_state = LUAT_FREETYPEFONT_STATE_ERROR;
+        return 0;
+    }
+    
+    // 保存字体路径 - 使用LuatOS内存管理
+    if (g_font_path) {
+        luat_heap_free(g_font_path);
+    }
+    size_t path_len = strlen(ttf_path);
+    g_font_path = (char*)luat_heap_malloc(path_len + 1);
+    if (g_font_path) {
+        memcpy(g_font_path, ttf_path, path_len + 1);
+    }
+    
+    g_ft_state = LUAT_FREETYPEFONT_STATE_INITED;
+    LLOGI("FreeType initialized with font: %s", ttf_path);
+    return 1;
+}
+
+void luat_freetypefont_deinit(void) {
+    if (g_ft_face) {
+        FT_Done_Face(g_ft_face);
+        g_ft_face = NULL;
+    }
+    
+    if (g_ft_lib) {
+        FT_Done_FreeType(g_ft_lib);
+        g_ft_lib = NULL;
+    }
+    
+    if (g_font_path) {
+        luat_heap_free(g_font_path);
+        g_font_path = NULL;
+    }
+    
+    g_ft_state = LUAT_FREETYPEFONT_STATE_UNINIT;
+    LLOGD("FreeType deinitialized");
+}
+
+luat_freetypefont_state_t luat_freetypefont_get_state(void) {
+    return g_ft_state;
+}
+
+unsigned int luat_freetypefont_get_char(
+    unsigned char *pBits,
+    unsigned char sty,
+    unsigned long fontCode,
+    unsigned char width,
+    unsigned char height,
+    unsigned char thick
+) {
+    (void)sty; (void)thick;
+    
+    if (!pBits || width == 0 || height == 0 || g_ft_state != LUAT_FREETYPEFONT_STATE_INITED) {
+        return 0;
+    }
+    
+    const uint32_t w = width;
+    const uint32_t h = height;
+    memset(pBits, 0, ((w + 7) / 8) * h);
+    
+    // 设置字体大小
+    if (FT_Set_Pixel_Sizes(g_ft_face, 0, h)) {
+        LLOGE("Failed to set pixel size");
+        return 0;
+    }
+    
+    // 加载字符
+    if (FT_Load_Char(g_ft_face, fontCode, FT_LOAD_RENDER)) {
+        // 字符不存在,尝试显示替换字符
+        if (fontCode != 0xFFFD && FT_Load_Char(g_ft_face, 0xFFFD, FT_LOAD_RENDER)) {
+            return 0;
+        }
+    }
+    
+    FT_GlyphSlot slot = g_ft_face->glyph;
+    FT_Bitmap* bm = &slot->bitmap;
+    
+    // 基线对齐
+    int off_x = 0;
+    int off_y = 0;
+    int asc_px = (int)(g_ft_face->size->metrics.ascender >> 6);
+    if (asc_px < 0) asc_px = 0;
+    if (asc_px > (int)h) asc_px = (int)h;
+    off_y = asc_px - (int)slot->bitmap_top;
+    
+    // 渲染到位图
+    for (int yy = 0; yy < (int)bm->rows; yy++) {
+        int dy = off_y + yy;
+        if (dy < 0 || dy >= (int)h) continue;
+        for (int xx = 0; xx < (int)bm->width; xx++) {
+            int dx = off_x + xx;
+            if (dx < 0 || dx >= (int)w) continue;
+            uint8_t val = bm->buffer[yy * bm->pitch + xx];
+            if (val > 127) {  // 阈值
+                set_bit_1bpp(pBits, w, (uint32_t)dx, (uint32_t)dy);
+            }
+        }
+    }
+    
+    // 计算字符宽度
+    uint32_t adv = (uint32_t)((slot->advance.x + 32) >> 6);
+    if (adv > w) adv = w;
+    if (adv < (uint32_t)bm->width) adv = (uint32_t)bm->width;
+    if (adv == 0) adv = (uint32_t)bm->width;
+    
+    return adv;
+}
+
+unsigned int* luat_freetypefont_get_char_gray(
+    unsigned char *pBits,
+    unsigned char sty,
+    unsigned long fontCode,
+    unsigned char fontSize,
+    unsigned char thick
+) {
+    (void)sty; (void)thick;
+    
+    // 设置默认值
+    g_gray_result[0] = 0;
+    g_gray_result[1] = 2;
+    
+    if (!pBits || fontSize == 0 || g_ft_state != LUAT_FREETYPEFONT_STATE_INITED) {
+        return g_gray_result;
+    }
+    
+    const uint32_t w = fontSize;
+    const uint32_t h = fontSize;
+    
+    // 根据字体大小选择灰度阶数
+    uint8_t bpp = (fontSize >= 16 && fontSize < 34) ? 4 : 2;
+    uint32_t bytes_per_row = ((w + 7) / 8) * bpp;
+    memset(pBits, 0, bytes_per_row * h);
+    
+    // 设置字体大小
+    if (FT_Set_Pixel_Sizes(g_ft_face, 0, h)) {
+        g_gray_result[1] = bpp;
+        return g_gray_result;
+    }
+    
+    // 加载字符
+    if (FT_Load_Char(g_ft_face, fontCode, FT_LOAD_RENDER)) {
+        // 字符不存在,尝试显示替换字符
+        if (fontCode != 0xFFFD && FT_Load_Char(g_ft_face, 0xFFFD, FT_LOAD_RENDER)) {
+            g_gray_result[1] = bpp;
+            return g_gray_result;
+        }
+    }
+    
+    FT_GlyphSlot slot = g_ft_face->glyph;
+    FT_Bitmap* bm = &slot->bitmap;
+    
+    // 基线对齐
+    int off_x = 0;
+    int off_y = 0;
+    int asc_px = (int)(g_ft_face->size->metrics.ascender >> 6);
+    if (asc_px < 0) asc_px = 0;
+    if (asc_px > (int)h) asc_px = (int)h;
+    off_y = asc_px - (int)slot->bitmap_top;
+    
+    // 渲染灰度位图
+    for (int yy = 0; yy < (int)bm->rows; yy++) {
+        int dy = off_y + yy;
+        if (dy < 0 || dy >= (int)h) continue;
+        for (int xx = 0; xx < (int)bm->width; xx++) {
+            int dx = off_x + xx;
+            if (dx < 0 || dx >= (int)w) continue;
+            uint8_t val = bm->buffer[yy * bm->pitch + xx];
+            if (val > 127) {
+                if (bpp == 4) {
+                    set_pix_gray(pBits, w, (uint32_t)dx, (uint32_t)dy, 4, 0x0F);
+                } else {
+                    set_pix_gray(pBits, w, (uint32_t)dx, (uint32_t)dy, 2, 0x03);
+                }
+            }
+        }
+    }
+    
+    // 计算字符宽度
+    uint32_t adv = (uint32_t)((slot->advance.x + 32) >> 6);
+    if (adv > w) adv = w;
+    if (adv < (uint32_t)bm->width) adv = (uint32_t)bm->width;
+    if (adv == 0) adv = (uint32_t)bm->width;
+    
+    g_gray_result[0] = adv;
+    g_gray_result[1] = bpp;
+    return g_gray_result;
+}
+
+unsigned int luat_freetypefont_get_str_width(
+    const char* str,
+    unsigned char fontSize
+) {
+    if (!str || fontSize == 0 || g_ft_state != LUAT_FREETYPEFONT_STATE_INITED) {
+        return 0;
+    }
+    
+    if (FT_Set_Pixel_Sizes(g_ft_face, 0, fontSize)) {
+        return 0;
+    }
+    
+    unsigned int total_width = 0;
+    const char* p = str;
+    
+    while (*p) {
+        uint32_t code = utf8_next_char(&p);
+        if (code == 0xFFFFFFFF) break;
+        
+        if (FT_Load_Char(g_ft_face, code, FT_LOAD_RENDER)) {
+            continue;  // 跳过不存在的字符
+        }
+        
+        uint32_t adv = (uint32_t)((g_ft_face->glyph->advance.x + 32) >> 6);
+        total_width += adv;
+    }
+    
+    return total_width;
+}
+
+int luat_freetypefont_draw_utf8(
+    int x,
+    int y,
+    const char* str,
+    unsigned char fontSize,
+    uint32_t color
+) {
+    if (!str || g_ft_state != LUAT_FREETYPEFONT_STATE_INITED) {
+        return -1;
+    }
+    
+    // 获取LCD设备指针
+    extern luat_lcd_conf_t* lcd_dft_conf;
+    if (!lcd_dft_conf) {
+        LLOGE("LCD not initialized");
+        return -1;
+    }
+    
+    if (FT_Set_Pixel_Sizes(g_ft_face, 0, fontSize)) {
+        return -1;
+    }
+    
+    int current_x = x;
+    const char* p = str;
+    
+    while (*p) {
+        uint32_t code = utf8_next_char(&p);
+        if (code == 0xFFFFFFFF) break;
+        
+        if (FT_Load_Char(g_ft_face, code, FT_LOAD_RENDER)) {
+            continue;  // 跳过不存在的字符
+        }
+        
+        FT_GlyphSlot slot = g_ft_face->glyph;
+        FT_Bitmap* bm = &slot->bitmap;
+        
+        // 基线对齐
+        int asc_px = (int)(g_ft_face->size->metrics.ascender >> 6);
+        if (asc_px < 0) asc_px = 0;
+        int off_y = asc_px - (int)slot->bitmap_top;
+        
+        // 绘制到LCD
+        for (int yy = 0; yy < (int)bm->rows; yy++) {
+            int dy = y + off_y + yy;
+            for (int xx = 0; xx < (int)bm->width; xx++) {
+                uint8_t val = bm->buffer[yy * bm->pitch + xx];
+                if (val > 127) {
+                    int dx = current_x + xx;
+                    luat_lcd_draw_point(lcd_dft_conf, dx, dy, color);
+                }
+            }
+        }
+        
+        // 更新X位置
+        current_x += (int)((slot->advance.x + 32) >> 6);
+    }
+    
+    return 0;
+}

+ 48 - 0
components/lcd/luat_lib_lcd.c

@@ -2021,6 +2021,51 @@ static int l_lcd_qspi_config(lua_State* L){
 	return 0;
 }
 
+#ifdef LUAT_USE_FREETYPEFONT
+/*
+使用FreeType字体绘制UTF-8字符串
+@api lcd.drawfreefontUtf8(x, y, str, fontSize, color)
+@int x 横坐标
+@int y 纵坐标(左下角为基准)
+@string str UTF-8字符串
+@int fontSize 字体大小(像素)
+@int color 颜色值(RGB565格式)
+@return nil 无返回值
+@usage
+-- 先初始化FreeType字体
+freetypefont.init("/sd/font.ttf")
+
+-- 绘制文本
+lcd.drawfreefontUtf8(10, 50, "Hello世界", 24, 0xFFFF)
+*/
+static int l_lcd_draw_freefont_utf8(lua_State *L) {
+    int x = luaL_checkinteger(L, 1);
+    int y = luaL_checkinteger(L, 2);
+    size_t len;
+    const char* str = luaL_checklstring(L, 3, &len);
+    int fontSize = luaL_checkinteger(L, 4);
+    uint32_t color = luaL_checkinteger(L, 5);
+    
+    if (lcd_dft_conf == NULL) {
+        LLOGE("LCD not initialized");
+        return 0;
+    }
+    
+    if (fontSize <= 0 || fontSize > 255) {
+        LLOGE("Invalid font size: %d", fontSize);
+        return 0;
+    }
+    
+    int result = luat_freetypefont_draw_utf8(x, y, str, (unsigned char)fontSize, color);
+    if (result != 0) {
+        LLOGE("Failed to draw UTF-8 string");
+    }
+    
+    lcd_auto_flush(lcd_dft_conf);
+    return 0;
+}
+#endif // LUAT_USE_FREETYPEFONT
+
 /*
 用户使用脚本初始化LCD完成后,必须调用本API
 @api lcd.user_done()
@@ -2123,6 +2168,9 @@ static const rotable_Reg_t reg_lcd[] =
     { "drawGtfontUtf8Gray", ROREG_FUNC(l_lcd_draw_gtfont_utf8_gray)},
 #endif // LUAT_USE_GTFONT_UTF8
 #endif // LUAT_USE_GTFONT
+#ifdef LUAT_USE_FREETYPEFONT
+    { "drawfreefontUtf8", ROREG_FUNC(l_lcd_draw_freefont_utf8)},
+#endif // LUAT_USE_FREETYPEFONT
     // 默认只带英文12号字体
     //@const font_opposansm12 font 12号字体
     { "font_opposansm12", ROREG_PTR((void*)u8g2_font_opposansm12)},

+ 1 - 0
luat/include/luat_libs.h

@@ -103,6 +103,7 @@ LUAMOD_API int luaopen_coremark( lua_State *L );
 
 LUAMOD_API int luaopen_fonts( lua_State *L );
 LUAMOD_API int luaopen_gtfont( lua_State *L );
+LUAMOD_API int luaopen_freetypefont( lua_State *L );
 
 LUAMOD_API int luaopen_pin( lua_State *L );
 LUAMOD_API int luaopen_pins( lua_State *L );