perf(cff): Local Subr INDEX 改轻量头部按需读 offset,subsetCFF -59%

subsetCFF 之前对命中的 Local Subr INDEX 做全量 readIndex(解析 count+1 个
offset),思源 FD12 26550 subr 这步耗 0.181ms,占 subsetCFF 一半。
但引用收集与重建只按 subr 编号读个别 offset(思源仅引用 13 个)。

新增 readIndexHeader(只读 count/offSize/dataStart + 末尾哨兵算 end)+
readIndexOffset(按编号 i 读单个 offSize 字节 offset),替换全量解析。

收益:
- subsetCFF 0.3466→0.1411ms (-59%)
- otf-思源黑体 woff2 min 1.50→1.30ms (-13%)、ttf 1.75→1.20ms (-31%)
- 输出字节完全一致(19188/10664 bytes),SSIM 保持 1.0000

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
崮生(子虚) 2026-07-25 01:05:35 +08:00
parent 70d3a2a3dc
commit 2834c4d4ab

View File

@ -55,6 +55,49 @@ function readIndex(b: Uint8Array, pos: number): CffIndex {
return { start: pos, count, offSize, offsets, dataStart, end: dataStart + offsets[count] - 1 };
}
/** INDEX count/offSize/dataStart (count+1) offset
* Local Subr INDEX subr offset FD12 26550 subr
* readIndex 26551 offset subsetCFF */
interface IndexHeader {
/** INDEX 起始偏移 */
start: number;
/** INDEX 内对象数量 */
count: number;
/** 偏移量字节宽度1~4 */
offSize: number;
/** 偏移量数组起始(紧接 count/offSize 之后) */
offBase: number;
/** 数据区起始(紧接偏移量数组之后 = offBase + (count+1)*offSize */
dataStart: number;
/** INDEX 结束位置(= 最后一个 object 的尾) */
end: number;
}
/** INDEX count + offSize + dataStart + end offset
* end count offset offBase + count*offSize */
function readIndexHeader(b: Uint8Array, pos: number): IndexHeader {
const count = (b[pos] << 8) | b[pos + 1];
/** count=0 的 INDEX 仅 2 字节 */
if (count === 0) return { start: pos, count: 0, offSize: 0, offBase: pos + 2, dataStart: pos + 2, end: pos + 2 };
const offSize = b[pos + 2];
const offBase = pos + 3;
const dataStart = offBase + (count + 1) * offSize;
/** 读第 count 个 offset哨兵= 总数据长 + 1算 end */
let lastOff = 0;
let op = offBase + count * offSize;
for (let j = 0; j < offSize; j++) lastOff = (lastOff << 8) | b[op++];
return { start: pos, count, offSize, offBase, dataStart, end: dataStart + lastOff - 1 };
}
/** object i INDEX i offset1-based offSize
* object i = [dataStart + offset(i) - 1, dataStart + offset(i+1) - 1) */
function readIndexOffset(b: Uint8Array, h: IndexHeader, i: number): number {
let v = 0;
let op = h.offBase + i * h.offSize;
for (let j = 0; j < h.offSize; j++) v = (v << 8) | b[op++];
return v;
}
/** INDEX [start, end) offset
* Local/Global Subr INDEX count+1 offset
* Local Subr subrreadIndex subsetCFF
@ -613,8 +656,8 @@ export function subsetCFF(cffBytes: Uint8Array, subsetGids: number[]): Uint8Arra
len: number;
/** Local Subr INDEX 原始字节(无则 null。思源等 CID 字体字形通过 callsubr 引用本地 subr */
localSubr: Uint8Array | null;
/** Local Subr INDEX 全量解析off→字节区间供引用收集与重建null 表示无 local subr */
localSubrIdx: CffIndex | null;
/** Local Subr INDEX 轻量头部(按需读 offset供引用收集与重建null 表示无 local subr */
localSubrIdx: IndexHeader | null;
/** 原 local subr biaslocalSubrIdx.count 决定) */
localBias: number;
/** 子集 local subr 旧索引→新索引映射引用收集后填充null 表示无需重映射(无 subr 或全保留) */
@ -651,14 +694,15 @@ export function subsetCFF(cffBytes: Uint8Array, subsetGids: number[]): Uint8Arra
const privDict = parseDict(b, privOrigOff, privOrigOff + privLen);
const subrRel = privDict.get(OP_LocalSubr)?.[0];
let localSubr: Uint8Array | null = null;
let localSubrIdx: CffIndex | null = null;
let localSubrIdx: IndexHeader | null = null;
let localBias = 0;
if (subrRel !== undefined) {
/** Local Subr INDEX 紧接 Private DICT 字节之后(绝对偏移 = privOrigOff + subrRel */
const subrAbs = privOrigOff + subrRel;
/** INDEX offset subr
* offset Private 1-2 FD subr */
localSubrIdx = readIndex(b, subrAbs);
/** count/offSize/dataStart (count+1) offset
* / subr offset 26550 subr
* readIndex subsetCFF */
localSubrIdx = readIndexHeader(b, subrAbs);
localBias = subrBias(localSubrIdx.count);
localSubr = b.subarray(localSubrIdx.start, localSubrIdx.end);
}
@ -693,8 +737,9 @@ export function subsetCFF(cffBytes: Uint8Array, subsetGids: number[]): Uint8Arra
if (!idx) continue;
const before = refs.size;
for (const sn of [...refs]) {
const ss = idx.dataStart + idx.offsets[sn] - 1;
const se = idx.dataStart + idx.offsets[sn + 1] - 1;
/** 按需读 offset避免全量解析 INDEX */
const ss = idx.dataStart + readIndexOffset(b, idx, sn) - 1;
const se = idx.dataStart + readIndexOffset(b, idx, sn + 1) - 1;
collectSubrRefs(b, ss, se, info.localBias, idx.count, refs, dummyGsubrRefs);
}
if (refs.size > before) changed = true;
@ -719,8 +764,9 @@ export function subsetCFF(cffBytes: Uint8Array, subsetGids: number[]): Uint8Arra
/** 重建 INDEX按新顺序写出被引用的 subr 字节。subr 内部 callsubr 也需重映射(递归 patch */
const objects: { bytes: Uint8Array; start: number; len: number }[] = [];
for (const oldSn of sortedRefs) {
const ss = idx.dataStart + idx.offsets[oldSn] - 1;
const se = idx.dataStart + idx.offsets[oldSn + 1] - 1;
/** 按需读 offset避免全量解析 INDEX */
const ss = idx.dataStart + readIndexOffset(b, idx, oldSn) - 1;
const se = idx.dataStart + readIndexOffset(b, idx, oldSn + 1) - 1;
const rewritten = rewriteCharstring(b, ss, se, info.localBias, remap, sortedRefs.length);
objects.push({ bytes: rewritten, start: 0, len: rewritten.length });
}