264 lines
10 KiB
C
264 lines
10 KiB
C
/* engine/rdft.c — FreeType 封装实现(见 engine/rdft.h)。
|
||
* 依赖 FreeType 2(静态篇 mipsel musl);无 SDK 依赖。
|
||
*/
|
||
#include "rdft.h"
|
||
#include <ft2build.h>
|
||
#include FT_FREETYPE_H
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
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);
|
||
}
|