From 68710d7ed814edf733afafbfaf3af2e797da4538 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Fri, 4 Sep 2026 21:01:56 +0800 Subject: [PATCH] =?UTF-8?q?fix(reader):=20=E4=B9=A6=E7=B4=A2=E5=BC=95build?= =?UTF-8?q?=5Findex=E5=85=A8=E9=87=8F=E9=80=90=E5=AD=97FreeType=E6=8E=92?= =?UTF-8?q?=E7=89=88=E4=BC=98=E5=8C=96=20=E2=80=94=20rdft=5Fadvance?= =?UTF-8?q?=E5=8A=A0(px,cp)->advance=E7=BC=93=E5=AD=98(=E5=BC=80=E6=94=BE?= =?UTF-8?q?=E5=AF=BB=E5=9D=804096=E6=A7=BD,=E4=B8=8Eglyph=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E5=88=86=E7=A6=BB,load=5Ffont/set=5Fpx=E5=90=8C?= =?UTF-8?q?=E6=B8=85),=20=E6=B5=8B=E9=87=8F=E4=BB=8EO(=E5=85=A8=E4=B9=A6?= =?UTF-8?q?=E5=AD=97=E7=AC=A6)=E9=99=8D=E5=88=B0O(=E5=94=AF=E4=B8=80?= =?UTF-8?q?=E7=A0=81=E7=82=B9~2600);=20=E5=A4=A7=E4=B9=A6(10MB)=E6=89=93?= =?UTF-8?q?=E5=BC=80=20rd=5Fbook=5Fopen=2023.9s->140ms(~170x),=20npages?= =?UTF-8?q?=E4=B8=80=E8=87=B4=E5=B8=83=E5=B1=80=E7=A1=AE=E5=AE=9A=E4=BF=9D?= =?UTF-8?q?=E7=95=99;=20=E7=82=B9=E6=9D=A1=E7=9B=AE=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=80=81busy=E5=BE=AA=E7=8E=AF=E5=86=BB?= =?UTF-8?q?=E6=AD=BB(=E9=9D=9E=E9=A9=B1=E5=8A=A8/=E9=94=81=E5=B1=8F,=20?= =?UTF-8?q?=E6=98=AFUI=E7=BA=BF=E7=A8=8B=E5=90=8C=E6=AD=A5=E5=85=A8?= =?UTF-8?q?=E9=87=8F=E5=BB=BA=E7=B4=A2=E5=BC=95);=200warning,=20=E5=AE=BF?= =?UTF-8?q?=E4=B8=BBtest=5Freader=5Fcore=2068-0/loaders=5Fsmoke/book=5Fsmo?= =?UTF-8?q?ke/formats=5Fsmoke=20PASS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- patch_v2/reader_app/engine/rdft.c | 54 +++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/patch_v2/reader_app/engine/rdft.c b/patch_v2/reader_app/engine/rdft.c index 73b1a3f..7b4ad54 100644 --- a/patch_v2/reader_app/engine/rdft.c +++ b/patch_v2/reader_app/engine/rdft.c @@ -15,6 +15,13 @@ typedef struct { 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; @@ -23,6 +30,7 @@ struct rdft { int nslots; /* 容量 */ int used; /* 在线条目数 */ uint64_t clock; /* 单调戳 */ + adv_slot *adv; /* advance 缓存(排版测量) */ int has_font; }; @@ -36,6 +44,8 @@ int rdft_init(rdft **r) 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; @@ -48,6 +58,7 @@ int rdft_load_font(rdft *r, const char *path) 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; @@ -61,8 +72,9 @@ void rdft_set_px(rdft *r, int px) if (px <= 0) px = r->px; r->px = px; if (r->face) FT_Set_Pixel_Sizes(r->face, 0, (FT_UInt)px); - /* key 含字号,清缓存防旧字号位图残留 */ + /* 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; } @@ -147,18 +159,53 @@ int rdft_glyph(rdft *r, uint32_t cp, int *adv_x, 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) { - *adv_x = r->face->glyph->advance.x >> 6; + 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); - *adv_x = a < 0 ? 0 : a; + a = a < 0 ? 0 : a; + *adv_x = a; + adv_put(r, key, a); return 0; } @@ -209,6 +256,7 @@ 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);