Files
MeowOS-For-R3P2/patch_v2/reader_app/engine/rdft.c
T
MeowOS 553a7d5d9e reader R1: os.meow.reader 书架+TXT 垂直链路骨架
- core: 编码检测(BOM→UTF-8严格→GB18030/Big5统计, Big5与GB重叠按GB18030兜底, §3.5风险注明)
  + GB18030双字节码表(uint16[126][190]=47880B, tools/gen_gb18030_table.py 生成, 生成物入仓)
  + 流式排版器(贪心断行+Kinsoku避头尾+分页+重排锚定, rd_meas_fn 测量回调替 FreeType, 宿主可测)
  + TXT 流式加载器(按段读, 不整文件入内存, \n 恒独立换行可安全分段)
- engine: FreeType 封装(FT_New_Face 按需读文件, NO_HINTING/LIGHT 灰阶, LRU 512 key=码点+字号,
  blend565, rdft_advance 只测宽不栅格化)
- app: 书架(扫 Books/ 识别 5 后缀, 最近读置顶, 空态提示) + 阅读页(W_CUSTOM 画布 3 栏翻页
  +中央菜单占位(字号±/行距±/返回) +顶栏进度%/章节名) + 书桥(段表+flat 页索引, 渲染, 重排回锚)
  + 进度持久化(MeowData/os.meow.reader/progress/, path hash→seg/ch/fs/ls/ts)
- 取舍: 正文字体 FreeType 直读 TF MeowOS/fonts/ TTF/OTF; 无字体回退 SDK 位图字库(meow_ui_text,
  未打包字体(许可/体积), 全 glyph 兜底 R2); 页位图缓存当前+前后各1页(3×691KB), 翻页动画默认无
- 内存实测(宿主 --wrap malloc, 73页书): 书结构 130KB + 当前段刮擦~1.6MB(行/页容量按 n 偏高,R2 收紧)
  + 字形缓存~650KB + 3页位图 2.07MB ≈ 4.45MB 动态, 在 ≤8MB 阅读态预算内
- 测试: 宿主 core 58/58(enc/gb18030金标准/kinsoku/page/reflow/txt); qemu-mipsel ft_smoke PASS
  + book_smoke(12页/渲染有墨/翻页/重排回锚) PASS; reader 交叉 -Wall -Wextra 0 warning
- 产物: reader(mipsel 静态1.2MB)+build_reader.sh(vendor FreeType 可复现); vendor/二进制 gitignore
2026-09-01 23:26:42 +08:00

216 lines
7.7 KiB
C
Raw 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;
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);
}