Files

264 lines
10 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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);
}