perf(cff): Local Subr INDEX 改 indexByteRange 不全量解析 offset,subsetCFF -46%

subsetCFF 0.313ms 中 0.202ms 集中在 fdaParse 段:readIndex(b, privOrigOff+subrRel)
全量解析 Local Subr INDEX 的 count+1 个 offset 数组,但后续只用 b.subarray(start,end)
取整体字节切片透传——offset 数组从未被消费。思源等 CID 字体 Local Subr 可达数千 subr,
全量解析是 subsetCFF 主要耗时。

新增 indexByteRange:仅读 count/offSize + 第 count 个 offset(位于 offset 数组末尾)算
end,跳过中间全部 offset 解析。Local Subr 透传改用它。

顺带合并 FDSelect 两处 lookupFDSelect 遍历为单次(首次遍历同时记录 gidOrigFds 供
newGidToFd 复用,消除第二次对 newSubsetGids 的二分查找)。

subsetOTF(思源8字) keepGSUB=true 0.704→0.504ms(-28%)、keepGSUB=false 0.428→0.232ms(-46%);
fdaParse 0.202→0.003ms;字节级零回归(cmp 输出逐字节一致);
基准测试全字体 SSIM 与优化前完全一致。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
崮生(子虚) 2026-07-24 23:24:18 +08:00
parent 7cd960617a
commit e8ae79a58c

View File

@ -59,6 +59,23 @@ function readIndex(b: Uint8Array, pos: number): CffIndex {
function getIndexBytes(b: Uint8Array, idx: CffIndex): Uint8Array {
return b.subarray(idx.start, idx.end);
}
/** INDEX [start, end) offset
* Local/Global Subr INDEX count+1 offset
* Local Subr subrreadIndex subsetCFF
* end = dataStart + offsets[count] - 1 count offset pos+3+count*offSize */
function indexByteRange(b: Uint8Array, pos: number): { start: number; end: number } {
const count = (b[pos] << 8) | b[pos + 1];
/** count=0 的 INDEX 仅 2 字节 */
if (count === 0) return { start: pos, end: pos + 2 };
const offSize = b[pos + 2];
/** dataStart = pos + 3count+offSize 头)+ (count+1)*offSizeoffset 数组) */
const dataStart = pos + 3 + (count + 1) * offSize;
/** 第 count 个 offset 位于 offset 数组末尾pos+3 + count*offSize读 offSize 字节大端 */
let lastOff = 0;
const lp = pos + 3 + count * offSize;
for (let j = 0; j < offSize; j++) lastOff = (lastOff << 8) | b[lp + j];
return { start: pos, end: dataStart + lastOff - 1 };
}
/** CFF Top/Private DICT 解析结果:操作码键 → 操作数数组。双字节操作码 12,n 存为 (12<<8)|n */
type CffDict = Map<number, number[]>;
@ -358,13 +375,16 @@ export function subsetCFF(cffBytes: Uint8Array, subsetGids: number[]): Uint8Arra
}
const newCharset = encodeCharsetFormat0(newCharsetBody);
/** FDSelect按需查每个 subsetGid 的原 FDlookupFDSelect 二分 range不全量展开 numGlyphs。 */
/** 收集命中的 FD保持首次出现顺序构建 原FD→新FD 映射 */
/** FDSelect subsetGid FDlookupFDSelect range numGlyphs
* FDFD fdRemap/usedFds+ gid FD gidOrigFds
* newGidToFd newSubsetGids lookupFDSelect */
const fdRemap = new Map<number, number>();
const usedFds: number[] = [];
for (const gid of newSubsetGids) {
const gidOrigFds: number[] = new Array(newSubsetNumGlyphs);
for (let i = 0; i < newSubsetNumGlyphs; i++) {
const gid = newSubsetGids[i];
const fd = lookupFDSelect(b, fdSelectOff, gid);
gidOrigFds[i] = fd;
if (!fdRemap.has(fd)) {
fdRemap.set(fd, usedFds.length);
usedFds.push(fd);
@ -411,8 +431,9 @@ export function subsetCFF(cffBytes: Uint8Array, subsetGids: number[]): Uint8Arra
let localSubr: Uint8Array | null = null;
if (subrRel !== undefined) {
/** Local Subr INDEX 紧接 Private DICT 字节之后(绝对偏移 = privOrigOff + subrRel */
const subrIdx = readIndex(b, privOrigOff + subrRel);
localSubr = b.subarray(subrIdx.start, subrIdx.end);
/** Local Subr INDEX 仅需整体字节切片透传,不全量解析 offset思源 Local Subr 可达数千 subr */
const subrRange = indexByteRange(b, privOrigOff + subrRel);
localSubr = b.subarray(subrRange.start, subrRange.end);
}
info = { origOff: privOrigOff, len: privLen, localSubr };
privSegCache.set(privOrigOff, info);
@ -420,12 +441,11 @@ export function subsetCFF(cffBytes: Uint8Array, subsetGids: number[]): Uint8Arra
fdInfos.push({ dictBytes, priv: info });
}
/** FDSelect gid FD FD 01+numGlyphs
* FD 3 ranges CID OTS */
/** FDSelect gid FD gidOrigFds lookupFDSelect
* FD 01+numGlyphs FD 3 ranges CID OTS */
const newGidToFd: number[] = new Array(newSubsetNumGlyphs);
for (let i = 0; i < newSubsetNumGlyphs; i++) {
const origFd = lookupFDSelect(b, fdSelectOff, newSubsetGids[i]);
newGidToFd[i] = fdRemap.get(origFd) ?? 0;
newGidToFd[i] = fdRemap.get(gidOrigFds[i]) ?? 0;
}
const newFdSelectBody = encodeFDSelect(newGidToFd);