/* engine/rdft.c — FreeType 封装实现(见 engine/rdft.h)。 * 依赖 FreeType 2(静态篇 mipsel musl);无 SDK 依赖。 */ #include "rdft.h" #include #include FT_FREETYPE_H #include #include typedef struct { uint64_t key; /* (px<<32)|cp */ uint64_t stamp; /* LRU 时间戳 */ int adv; /* 水平 advance(px) */ uint8_t *bmp; /* 灰阶位图拷贝(malloc;bw==0 时 NULL) */ int bw, bh, pitch, left, top; } glyph_slot; struct rdft { FT_Library lib; FT_Face face; int px; glyph_slot *slots; int nslots; /* 容量 */ int used; /* 在线条目数 */ uint64_t clock; /* 单调戳 */ int has_font; }; int rdft_init(rdft **r) { if (!r) return -1; rdft *x = (rdft *)calloc(1, sizeof *x); if (!x) return -6; if (FT_Init_FreeType(&x->lib) != 0) { free(x); return -3; } x->px = 20; x->nslots = 512; x->slots = (glyph_slot *)calloc((size_t)x->nslots, sizeof(glyph_slot)); if (!x->slots) { FT_Done_FreeType(x->lib); free(x); return -6; } x->used = 0; x->clock = 1; *r = x; return 0; } int rdft_load_font(rdft *r, const char *path) { if (!r || !path) return -1; if (r->face) { FT_Done_Face(r->face); r->face = NULL; } if (r->slots) { for (int i = 0; i < r->nslots; i++) free(r->slots[i].bmp); } if (r->slots) memset(r->slots, 0, (size_t)r->nslots * sizeof(glyph_slot)); r->used = 0; if (FT_New_Face(r->lib, path, 0, &r->face) != 0) { r->has_font = 0; return -1; } r->has_font = 1; if (r->px > 0) FT_Set_Pixel_Sizes(r->face, 0, r->px); return 0; } void rdft_set_px(rdft *r, int px) { if (!r) return; if (px <= 0) px = r->px; r->px = px; if (r->face) FT_Set_Pixel_Sizes(r->face, 0, (FT_UInt)px); /* key 含字号,清缓存防旧字号位图残留 */ if (r->slots) { for (int i = 0; i < r->nslots; i++) free(r->slots[i].bmp); memset(r->slots, 0, (size_t)r->nslots * sizeof(glyph_slot)); } r->used = 0; } int rdft_ascender(rdft *r) { if (!r || !r->face) return 0; return (int)(r->face->size->metrics.ascender >> 6); } int rdft_descender(rdft *r) { if (!r || !r->face) return 0; return (int)(r->face->size->metrics.descender >> 6); } const char *rdft_family(rdft *r) { if (!r || !r->face || !r->face->family_name) return ""; return r->face->family_name; } static glyph_slot *slot_find(rdft *r, uint64_t key) { for (int i = 0; i < r->nslots; i++) if (r->slots[i].key == key && r->slots[i].stamp) return &r->slots[i]; return NULL; } static glyph_slot *slot_lru_evict(rdft *r) { /* 找未用槽(stamp==0)或最旧使用 */ glyph_slot *oldest = NULL; uint64_t mins = UINT64_MAX; for (int i = 0; i < r->nslots; i++) { if (!r->slots[i].stamp) { r->used++; return &r->slots[i]; } /* 空槽 */ if (r->slots[i].stamp < mins) { mins = r->slots[i].stamp; oldest = &r->slots[i]; } } free(oldest->bmp); oldest->bmp = NULL; oldest->key = 0; oldest->stamp = 0; return oldest; } int rdft_glyph(rdft *r, uint32_t cp, int *adv_x, const uint8_t **bitmap, int *bw, int *bh, int *left, int *top) { if (!r || !r->face || !adv_x) return -1; if (r->clock == UINT64_MAX) { r->clock = 1; } /* 时钟回绕(罕见) */ uint64_t key = ((uint64_t)(uint32_t)r->px << 32) | cp; glyph_slot *s = slot_find(r, key); if (s) { s->stamp = r->clock++; *adv_x = s->adv; *bitmap = s->bmp; *bw = s->bw; *bh = s->bh; *left = s->left; *top = s->top; return 0; } /* miss:光栅化 */ FT_UInt gi = FT_Get_Char_Index(r->face, cp); if (r->px <= 0) FT_Set_Pixel_Sizes(r->face, 0, (FT_UInt)r->px); if (FT_Load_Glyph(r->face, gi, FT_LOAD_RENDER | FT_LOAD_NO_HINTING | FT_LOAD_TARGET_LIGHT) != 0) { /* 空字形(缺字)→ 仅 advance */ *adv_x = r->face->glyph->advance.x >> 6; *bitmap = NULL; *bw = *bh = *left = *top = 0; return 0; } FT_GlyphSlot g = r->face->glyph; int a = (int)((g->advance.x >= 0 ? g->advance.x + 0x20 : g->advance.x - 0x20) >> 6); if (a < 0) a = 0; s = slot_lru_evict(r); s->key = key; s->stamp = r->clock++; s->adv = a; s->bmp = NULL; s->bw = 0; s->bh = 0; s->pitch = 0; s->left = 0; s->top = 0; if (g->bitmap.buffer && g->bitmap.width > 0 && g->bitmap.rows > 0) { int w = (int)g->bitmap.width, rows = (int)g->bitmap.rows, pitch = (int)g->bitmap.pitch; uint8_t *b = (uint8_t *)malloc((size_t)w * (size_t)rows); if (b) { /* 紧凑拷贝(每行 = w 字节),blend 用 bw 作行步长 */ for (int r_ = 0; r_ < rows; r_++) memcpy(b + (size_t)r_ * (size_t)w, g->bitmap.buffer + (size_t)r_ * (size_t)pitch, (size_t)w); s->bmp = b; s->bw = w; s->bh = rows; s->pitch = w; s->left = g->bitmap_left; s->top = g->bitmap_top; } } *adv_x = s->adv; *bitmap = s->bmp; *bw = s->bw; *bh = s->bh; *left = s->left; *top = s->top; return 0; } int rdft_advance(rdft *r, uint32_t cp, int *adv_x) { if (!r || !r->face || !adv_x) return -1; FT_UInt gi = FT_Get_Char_Index(r->face, cp); if (r->px <= 0) FT_Set_Pixel_Sizes(r->face, 0, (FT_UInt)r->px); if (FT_Load_Glyph(r->face, gi, FT_LOAD_NO_HINTING | FT_LOAD_TARGET_LIGHT) != 0) { *adv_x = r->face->glyph->advance.x >> 6; return 0; } int a = (int)((r->face->glyph->advance.x >= 0 ? r->face->glyph->advance.x + 0x20 : r->face->glyph->advance.x - 0x20) >> 6); *adv_x = a < 0 ? 0 : a; return 0; } void rdft_blend565(uint16_t *fb, int pitch_bytes, int tw, int th, int x, int y, const uint8_t *bmp, int bw, int bh, int left, int top, uint16_t fg565, uint16_t bg565) { if (!fb || !bmp || bw <= 0 || bh <= 0) return; int fg_r = (fg565 >> 11) & 0x1F, fg_g = (fg565 >> 5) & 0x3F, fg_b = fg565 & 0x1F; int bg_r = (bg565 >> 11) & 0x1F, bg_g = (bg565 >> 5) & 0x3F, bg_b = bg565 & 0x1F; int start_x = x + left, base_y = y; /* y 为基线 */ int start_y = base_y - top; int pitch_px = pitch_bytes / 2; /* 目标每行像素数 */ for (int row = 0; row < bh; row++) { int ty = start_y + row; if (ty < 0) continue; if (ty >= th) break; const uint8_t *src = bmp + (size_t)row * (size_t)bw; /* 紧凑:每行 bw */ for (int col = 0; col < bw; col++) { int tx = start_x + col; if (tx < 0) continue; if (tx >= tw) break; unsigned a = src[col]; if (a == 0) continue; uint16_t *px = fb + (size_t)ty * (size_t)pitch_px + tx; int r = (fg_r * a + bg_r * (255 - a)) / 255; int g = (fg_g * a + bg_g * (255 - a)) / 255; int b = (fg_b * a + bg_b * (255 - a)) / 255; *px = (uint16_t)((r << 11) | (g << 5) | b); } } } void rdft_cache_set_slots(rdft *r, int slots) { if (!r || slots < 0 || slots == r->nslots) return; glyph_slot *na = (glyph_slot *)calloc((size_t)(slots ? slots : 1), sizeof(glyph_slot)); if (!na) return; for (int i = 0; i < r->nslots; i++) free(r->slots[i].bmp); free(r->slots); r->slots = na; r->nslots = (slots ? slots : 1); r->used = 0; } int rdft_cache_slots(rdft *r) { return r ? r->nslots : 0; } int rdft_cache_used(rdft *r) { return r ? r->used : 0; } void rdft_free(rdft *r) { if (!r) return; if (r->slots) { for (int i = 0; i < r->nslots; i++) free(r->slots[i].bmp); } free(r->slots); if (r->face) FT_Done_Face(r->face); if (r->lib) FT_Done_FreeType(r->lib); free(r); }