崮生(子虚)
|
2c56d2cbb2
|
perf(font): 轻量 GSUB probe 跳过 glyf 解析,CJK 子集化提速 17%~56%
fontSubset 原先对 probe 阶段也调用完整 Font.create(subset 模式),
它会解析子集 glyph 的 glyf 轮廓(parseSimpleGlyf)。但 probe 只需要
GSUB(原始字节透传)和 cmap(codepoint→gid),根本不需要轮廓。
实测 CJK 字体(思源黑体/令东齐伋)两次 Font.create 占总耗时 36%~63%。
新增 gsub-probe.ts:直接从字体字节解析表目录,提取 GSUB 字节切片 +
cmap 的 codepoint→gid 查找(format4 BMP + format12 补充平面二分,
与 fonteditor-core readWindowsAllCodes 优先级一致),完全跳过 glyf。
三种路径(与原 Font.create probe 行为严格等价):
- otf / ttf 无 GSUB:直接判无 reachable(otf 的 fonteditor probe 本就
origGSUB=undefined),连 probe Font.create 也跳过
- ttf 有 GSUB 且 cmap 含 fmt4/12:轻量 probe 算 reachable
- ttf 有 GSUB 但 cmap 无 fmt4/12(极罕见):回退 Font.create probe
验证:
- seedGids / GSUB字节 / reachable 与原 probe 逐项等价(FiraCode 87 target 等)
- 5 个代表字体 woff2 输出与优化前逐字节一致(cmp 通过)
- pnpx tsx 基准测试.test.ts 全部 SSIM 无回归:
思源黑体-千字文中段 7.8→4.5ms(-42%)、千字文前段 11.9→8.9ms(-25%)、
otf-思源 2.9→1.9ms(-34%)、霞鹜文楷-汉字标点 5.0→2.2ms(-56%)、
FiraCode 18.5→17.2ms,SSIM 全部不变
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-23 17:41:16 +08:00 |
|
崮生(子虚)
|
2da35e4257
|
perf(gsub): emitCoverage 去掉冗余的 new Set 去重
emitCoverage 原用 Array.from(new Set(newGids)).sort() 去重+排序。
但所有调用方传入的 newGids 逻辑上保证无重复:
- readCoverageRemapped 按原 coverage(规范要求 gid 唯一)升序过滤,每个原 gid 映射唯一新 gid
- entries.map(e=>e.from) 的 from 是 entry 主键(唯一)
实测 FiraCode 单次子集化 327 次 emitCoverage 调用 0 次发现重复,
new Set 去重纯属 GC 开销(Set 构造+迭代+Array.from)。
改为 newGids.slice().sort(),消除 Set 分配。sort 保留(新 gid 随原 gid 非单调,需排序)。
验证:与优化前字节逐字节一致(cmp 通过,8724==8724),
pnpx tsx 基准测试.test.ts 全部 SSIM 无回归。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-23 17:16:45 +08:00 |
|
崮生(子虚)
|
5da4cd5b06
|
perf(gsub): fmt3 ChainContext 预检跳过 + 修复 covCache 共享语义污染
预检优化(type1-4 + fmt1 + fmt3 全覆盖):
- 重构 isPrimaryCoverageOutOfSubset → isSubtableSkipableByCoverage
+ 抽出 coverageAllOutOfSubset 辅助函数(内联遍历,不分配数组、不碰缓存)
- 新增 ChainContext format3 预检:遍历 back/input/look 三组 coverage,
任一组「原非空且全子集外」即判定 subtable 失效,跳过深度解析
- FiraCode 403 lookup 中 ~330 个可预检跳过,子集化 23ms→16ms(-31%)
covCache 共享语义污染修复(fmt3 预检能安全启用的前提):
- CoverageCacheEntry 的 gids(原始 gid,fmt1 按 index 配对)与
remapped(newGids,fmt3 序列化)两种语义原混写同一字段:
旧 readCoverageRemapped 把 newGids 写进 gids,污染后续 fmt1 的
readCoverageGids(拿到 newGids 当原始 gid 二次重映射,连字规则错位)
- 修复:readCoverageRemapped 只写 remapped,gids 留单例占位 EMPTY_GIDS;
readCoverageGids 见占位即重新计算并 mergeGidsEntry(保留已有 remapped)
- 修复后 fmt3 预检开关下字节完全等价(8724==8724),SSIM 全程无回归
验证:pnpx tsx 基准测试.test.ts 全部 SSIM 无回归
(FiraCode=0.9923, otf-思源黑体=0.9310, 初夏明朝-纯标点=0.9920 不变)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-23 17:05:46 +08:00 |
|
崮生(子虚)
|
25da274c3a
|
perf(gsub): subsetGSUB 主 coverage 预检跳过空 lookup,FiraCode 33ms→22ms
FiraCode 含 403 个 lookup,子集仅命中少数字形,多数 lookup 主 coverage 引用
的 gid 全部不在子集内——深度序列化后必然得到空 entries、回退 writeEmptySubtable。
预检在深度解析前用 gidLookup 内联判定主 coverage 是否全子集外,命中即跳过
(type1-4 + type6-format1,共 ~216 个空 lookup),消除其 entries 构建/rule 解析开销。
关键正确性:
- format2(class 驱动)/format3(多 coverage)不预检——主 coverage 非充分条件,
误判全空会丢连字(实测 FiraCode calt format2,SSIM 0.9923→0.9368)
- 预检不经 covCache——该缓存被 readCoverageRemapped(存 newGids)与
readCoverageGids(format1 期望原始 gid)共享,语义不一致,预检写入会污染
format1 深度解析(newGids 被当原始 gid 二次重映射,连字规则错位)
- 输出字节与预检关闭时完全等价(已 dump 对比验证),SSIM 全部无回归
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-23 16:45:43 +08:00 |
|
崮生(子虚)
|
771f4fc6de
|
perf(gsub): GidLookup 用 number[] + format1 预分配,FiraCode 30ms→23ms
- GidLookup 从 Int32Array 改为 number[]:实测 V8 下普通数组索引访问比
TypedArray 快约 2×(coverage 过滤高频查询)
- readCoverageRemapped format1 预分配 newGids(count) 索引写入后截断,
避免 push 动态扩容
SSIM 0.9923 保持,输出 8708B 稳定。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-23 13:38:05 +08:00 |
|
崮生(子虚)
|
28b6b1ebf4
|
perf(gsub): subsetGSUB 缓存+数组查找+裸读,FiraCode 983ms→30ms
FiraCode-代码用例(403 lookup)子集化从 983ms 降至 ~30ms,SSIM 全程保持 0.9923。
核心优化(CPU profile 逐层下钻):
- coverage 解析+重映射缓存(off→{gids,remapped,outOfSubset}):format3 的 coverage
被多 subtable 引用,解析与重映射结果按 off 复用;readCoverageRemapped 边解析边过滤,
format3 不再分配中间原 gid 数组
- OTReader.dv 公开:coverage 偏移来自已验证 subtable,热循环用裸 DataView.getUint16
绕过 u16 的逐次边界检查
- GidLookup Int32Array(下标=原gid,值=新gid,-1=不在子集)替代 Map.get,coverage 过滤
的高频查询从哈希退化为数组索引
- OTWriter.rollback 改 pop:patches 按 pos 单调追加,回退从末尾 pop 替代全量 filter
(FiraCode 每次 rollback 392 次)
- gsub-reachable coverage 缓存:不动点迭代每轮重复解析同一 coverage,按 off 缓存
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-23 13:33:19 +08:00 |
|
崮生(子虚)
|
a84a4aa6f2
|
feat(gsub): GSUB/GPOS 布局表子集化 + FiraCode 连字修复
实现 GSUB/GPOS 的 gid 重映射子集化,解决 CJK 标点压缩丢失与连字不渲染问题。
核心改动:
- ot-bytes.ts: 共享 OTWriter/OTReader + ScriptList/FeatureList 紧凑重排
- gpos-subset.ts: SinglePos/PairPos coverage/ClassDef gid 重映射
- gsub-subset.ts: type1/2/3/4/6 coverage/ClassDef/替换目标 gid 重映射
- gsub-reachable.ts: GSUB 替换链不动点迭代,收集无 unicode 的连字 target 字形
- GSUB.js + support/ttfreader/ttfwriter: GSUB 字节透传读写接入
- parse.js: 修复 F2DOT14 复合字形解码(负数 +0.5|0 截断 bug)
关键 bug 修复:
- ChainContextSubst format2 字段错位:漏读 coverageOffset 致 chainSubClassSetCount
读成 lookaheadClassDefOffset,连字核心规则降级为空。修复后 FiraCode <= 走对
spacer+liga 路径。
- ScriptList/FeatureList 物理交错 + subtable 散落致 GSUB 膨胀损坏,改遍历重排。
SSIM: FiraCode-代码 0.9636 → 0.9923(与 pyftsubset 黄金像素级一致),
CJK 标点压缩全面恢复(初夏明朝纯标点 0.7950→0.9920 等)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2026-07-23 13:01:03 +08:00 |
|
崮生(子虚)
|
bd3189b459
|
优化性能
|
2026-04-11 17:53:57 +08:00 |
|
崮生(子虚)
|
6ba093ff70
|
优化性能
|
2026-04-11 10:25:54 +08:00 |
|
崮生(子虚)
|
161bafc02a
|
实现 js 版 woff2 处理
|
2026-04-10 20:42:30 +08:00 |
|
崮生(子虚)
|
18e60fe940
|
优化性能
|
2026-04-09 16:47:41 +08:00 |
|
崮生(子虚)
|
820fc71166
|
feat: 新增 woff2 输出格式支持,默认使用 woff2
- 后端支持 outType URL 参数(woff2/ttf),Node.js 默认 woff2,LLRT 默认 ttf
- woff2 wasm 延迟初始化,仅首次请求时加载(~8ms)
- SDK 三个入口函数支持 outType 选项,默认 woff2
- 前端 CSS、下载、使用说明适配 woff2
- 基准测试新增 woff2 测试(耗时、体积、压缩率)
- 构建脚本自动复制 woff2.wasm 到 dist 目录
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
2026-04-09 11:48:07 +08:00 |
|
崮生(子虚)
|
765a301649
|
修改为本地引用,清理冗余文件
|
2026-04-09 09:50:26 +08:00 |
|
崮生(子虚)
|
b902b4e1cc
|
增强安全性,重构代码
|
2026-04-08 22:05:17 +08:00 |
|
zixu
|
a1b5502891
|
支持使用 tjs 运行
|
2024-08-19 18:10:09 +08:00 |
|
zixu
|
6b896d768e
|
init
|
2024-08-18 15:14:50 +08:00 |
|