/* 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; /* advance 测量缓存:rdft_advance 逐字测量是排版索引用热路径(大书每次开/重排全量逐字测), * 同一码点在全书反复出现(中文常用字 ~几千)。独立的小型开放寻址表缓存 (px,cp)→advance, * 避免对重复码点反复 FT_Load_Glyph,把测量成本从 O(全书字符) 摊到 O(唯一码点)。 * 与 glyph_slot 缓存分离:glyph 缓存存「已光栅化位图」,此处只存 advance(不栅格化、不占位图)。 */ #define RD_ADV_SLOTS 4096 /* 幂 2;开放寻址线性探测 */ typedef struct { uint64_t key; int adv; } adv_slot; struct rdft { FT_Library lib; FT_Face face; int px; glyph_slot *slots; int nslots; /* 容量 */ int used; /* 在线条目数 */ uint64_t clock; /* 单调戳 */ adv_slot *adv; /* advance 缓存(排版测量) */ 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->adv = (adv_slot *)calloc(RD_ADV_SLOTS, sizeof(adv_slot)); if (!x->adv) { free(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)); if (r->adv) memset(r->adv, 0, (size_t)RD_ADV_SLOTS * sizeof(adv_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 含字号,清缓存防旧字号位图残留(glyph + advance 同清,key 同含 px) */ 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)); } if (r->adv) memset(r->adv, 0, (size_t)RD_ADV_SLOTS * sizeof(adv_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; } /* (px,cp)→advance 缓存:开放寻址线性探测;key==0 为空槽哨兵(key=(px<<32)|cp,px≥1 恒非 0)。 */ static uint32_t adv_hash(uint64_t key) { /* 乘法散列取高位,把 64 位 key(px 在高位、cp 在低位)散布到 12 位表。 */ return (uint32_t)((key * 2654435761ULL) >> 22) & (RD_ADV_SLOTS - 1); } static int adv_find(const rdft *r, uint64_t key, int *adv) { uint32_t h = adv_hash(key); for (uint32_t i = 0; i < RD_ADV_SLOTS; i++) { uint32_t idx = (h + i) & (RD_ADV_SLOTS - 1); uint64_t k = r->adv[idx].key; if (k == 0) return 0; if (k == key) { *adv = r->adv[idx].adv; return 1; } } return 0; } static void adv_put(rdft *r, uint64_t key, int adv) { uint32_t h = adv_hash(key); for (uint32_t i = 0; i < RD_ADV_SLOTS; i++) { uint32_t idx = (h + i) & (RD_ADV_SLOTS - 1); if (r->adv[idx].key == 0 || r->adv[idx].key == key) { r->adv[idx].key = key; r->adv[idx].adv = adv; return; } } /* 表满:丢弃(下次未命中直接算) */ } int rdft_advance(rdft *r, uint32_t cp, int *adv_x) { if (!r || !r->face || !adv_x) return -1; uint64_t key = ((uint64_t)(uint32_t)r->px << 32) | cp; if (adv_find(r, key, adv_x)) return 0; 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) { int a = r->face->glyph->advance.x >> 6; *adv_x = a; adv_put(r, key, a); return 0; } int a = (int)((r->face->glyph->advance.x >= 0 ? r->face->glyph->advance.x + 0x20 : r->face->glyph->advance.x - 0x20) >> 6); a = a < 0 ? 0 : a; *adv_x = a; adv_put(r, key, 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); free(r->adv); if (r->face) FT_Done_Face(r->face); if (r->lib) FT_Done_FreeType(r->lib); free(r); }