mirror of
https://github.com/2234839/web-font.git
synced 2026-09-11 11:15:51 +08:00
perf(cmap): subset 模式 format4 延迟解析,跳过 4×segCount 数组构建
subset 模式下 format4 不再急切构建 endCode/startCode/idDelta/idRangeOffset 共 4 个 segCount 项数组,只记录各段在 view 的偏移;lookupFormat4 二分时 按需从 view 读(mid*2 偏移)。原 subset 模式仍执行 4×segCount 次 getUint16 + 4 个数组分配,得意黑 segCount=3929 → 1.5 万次读,占 readSubTable 0.101ms。 延迟后 view 读次数 = O(S × log(segCount)),远小于 4×segCount。 lookupFormat4 新增 _lazySegs 分支,非延迟路径(全量模式)保持急切数组不变。 idDelta 仍用 getUint16 读(与原急切模式一致),依赖 % 0x10000 处理符号。 字节逐字节一致(令东/思源/初夏/得意黑/otf 全 cmp 通过),SSIM 全程不变。 cmap: 初夏 0.075→0.009ms(-88%),得意黑→0.004ms(-94%),思源 0.063→0.014(-78%)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b7dbe3fe1d
commit
65e238cdca
@ -88,44 +88,64 @@ function readSubTable(reader, ttf, subTable, cmapOffset) {
|
||||
format4.entrySelector = view.getUint16(vOffset, false); vOffset += 2;
|
||||
format4.rangeShift = view.getUint16(vOffset, false); vOffset += 2;
|
||||
var segCount = format4.segCountX2 / 2;
|
||||
|
||||
var endCode = new Array(segCount);
|
||||
for (var e = 0; e < segCount; e++) {
|
||||
endCode[e] = view.getUint16(vOffset, false);
|
||||
vOffset += 2;
|
||||
}
|
||||
format4.endCode = endCode;
|
||||
format4.reservedPad = view.getUint16(vOffset, false); vOffset += 2;
|
||||
|
||||
var startCode = new Array(segCount);
|
||||
for (var s = 0; s < segCount; s++) {
|
||||
startCode[s] = view.getUint16(vOffset, false);
|
||||
vOffset += 2;
|
||||
}
|
||||
format4.startCode = startCode;
|
||||
|
||||
var idDelta = new Array(segCount);
|
||||
for (var d = 0; d < segCount; d++) {
|
||||
idDelta[d] = view.getUint16(vOffset, false);
|
||||
vOffset += 2;
|
||||
}
|
||||
format4.idDelta = idDelta;
|
||||
format4.idRangeOffsetOffset = vOffset - view.byteOffset;
|
||||
|
||||
var idRangeOffset = new Array(segCount);
|
||||
for (var r = 0; r < segCount; r++) {
|
||||
idRangeOffset[r] = view.getUint16(vOffset, false);
|
||||
vOffset += 2;
|
||||
}
|
||||
format4.idRangeOffset = idRangeOffset;
|
||||
format4.segCount = segCount;
|
||||
|
||||
/* 优化101: subset 模式下跳过 glyphIdArray 解析,直接从 view 按需读取 */
|
||||
/* 优化(format4 subset 延迟解析):subset 模式下不展开 4 个 segCount 数组,
|
||||
* 只记录各段在 view 的偏移,lookupFormat4 二分时按需从 view 读。
|
||||
* 原 subset 模式仍构建 endCode/startCode/idDelta/idRangeOffset 共 4×segCount 次 getUint16,
|
||||
* 得意黑 segCount=3929 → 1.5 万次读 + 4 个数组分配,占 readSubTable 0.101ms。
|
||||
* subset 仅查找少数 cp,延迟后 view 读次数 = O(S × log(segCount)),远小于 4×segCount。
|
||||
* 非 subset 模式仍需全量遍历 segments,保持急切构建数组。 */
|
||||
var isSubset4 = ttf.readOptions && ttf.readOptions.subset;
|
||||
if (isSubset4) {
|
||||
format4.glyphIdArrayOffset = vOffset - view.byteOffset;
|
||||
/* endCode 段起始(相对 view.byteOffset),紧接 14B header */
|
||||
var endCodeOff = vOffset;
|
||||
/* startCode 段 = endCode 段后 + reservedPad(2) */
|
||||
var startCodeOff = endCodeOff + segCount * 2 + 2;
|
||||
var idDeltaOff = startCodeOff + segCount * 2;
|
||||
var idRangeOffsetOff = idDeltaOff + segCount * 2;
|
||||
var glyphIdArrayOff = idRangeOffsetOff + segCount * 2;
|
||||
format4._cmapView = view;
|
||||
format4._lazySegs = true;
|
||||
format4._endCodeOff = endCodeOff;
|
||||
format4._startCodeOff = startCodeOff;
|
||||
format4._idDeltaOff = idDeltaOff;
|
||||
format4._idRangeOffsetOff = idRangeOffsetOff;
|
||||
format4.idRangeOffsetOffset = idRangeOffsetOff - view.byteOffset;
|
||||
format4.glyphIdArrayOffset = glyphIdArrayOff - view.byteOffset;
|
||||
/* 优化177: 预计算 glyphIdArrayIndexOffset,消除 lookupFormat4 中的重复除法 */
|
||||
format4.glyphIdArrayIndexOffset = (format4.glyphIdArrayOffset - format4.idRangeOffsetOffset) / 2;
|
||||
} else {
|
||||
var endCode = new Array(segCount);
|
||||
for (var e = 0; e < segCount; e++) {
|
||||
endCode[e] = view.getUint16(vOffset, false);
|
||||
vOffset += 2;
|
||||
}
|
||||
format4.endCode = endCode;
|
||||
format4.reservedPad = view.getUint16(vOffset, false); vOffset += 2;
|
||||
|
||||
var startCode = new Array(segCount);
|
||||
for (var s = 0; s < segCount; s++) {
|
||||
startCode[s] = view.getUint16(vOffset, false);
|
||||
vOffset += 2;
|
||||
}
|
||||
format4.startCode = startCode;
|
||||
|
||||
var idDelta = new Array(segCount);
|
||||
for (var d = 0; d < segCount; d++) {
|
||||
idDelta[d] = view.getUint16(vOffset, false);
|
||||
vOffset += 2;
|
||||
}
|
||||
format4.idDelta = idDelta;
|
||||
format4.idRangeOffsetOffset = vOffset - view.byteOffset;
|
||||
|
||||
var idRangeOffset = new Array(segCount);
|
||||
for (var r = 0; r < segCount; r++) {
|
||||
idRangeOffset[r] = view.getUint16(vOffset, false);
|
||||
vOffset += 2;
|
||||
}
|
||||
format4.idRangeOffset = idRangeOffset;
|
||||
|
||||
var glyphCount4 = (format4.length - (vOffset - view.byteOffset - startOffset)) / 2;
|
||||
format4.glyphIdArrayOffset = vOffset - view.byteOffset;
|
||||
|
||||
@ -135,9 +155,9 @@ function readSubTable(reader, ttf, subTable, cmapOffset) {
|
||||
vOffset += 2;
|
||||
}
|
||||
format4.glyphIdArray = glyphIdArray4;
|
||||
/* 优化177: 预计算 glyphIdArrayIndexOffset */
|
||||
format4.glyphIdArrayIndexOffset = (format4.glyphIdArrayOffset - format4.idRangeOffsetOffset) / 2;
|
||||
}
|
||||
/* 优化177: 预计算 glyphIdArrayIndexOffset,消除 lookupFormat4 中的重复除法 */
|
||||
format4.glyphIdArrayIndexOffset = (format4.glyphIdArrayOffset - format4.idRangeOffsetOffset) / 2;
|
||||
} else if (subTable.format === 6) {
|
||||
var format6 = subTable;
|
||||
format6.length = view.getUint16(vOffset, false); vOffset += 2;
|
||||
|
||||
@ -57,38 +57,74 @@ function lookupFormat12(format12, unicode) {
|
||||
/**
|
||||
* 优化114: format4 二分查找 segment,替代线性扫描
|
||||
* 优化293: 接受预计算的 graphIdArrayIndexOffset 参数,避免每次调用重复计算
|
||||
* 优化(subset 延迟):_lazySegs 模式下各段未展开为数组,二分时按需从 view 读取
|
||||
*/
|
||||
function lookupFormat4(format4, unicode, _graphIdArrayIndexOffset) {
|
||||
var segCount = format4.segCount || (format4.segCountX2 / 2);
|
||||
|
||||
/* 延迟模式:各段在 view 中连续,按 mid*2 偏移读取。命中后再读 idDelta/idRangeOffset */
|
||||
if (format4._lazySegs) {
|
||||
var view = format4._cmapView;
|
||||
var endOff = format4._endCodeOff;
|
||||
var startOff = format4._startCodeOff;
|
||||
var deltaOff = format4._idDeltaOff;
|
||||
var rangeOff = format4._idRangeOffsetOff;
|
||||
var lo = 0, hi = segCount - 1;
|
||||
while (lo <= hi) {
|
||||
var mid = (lo + hi) >> 1;
|
||||
var m2 = mid * 2;
|
||||
var sCode = view.getUint16(startOff + m2, false);
|
||||
if (unicode < sCode) {
|
||||
hi = mid - 1;
|
||||
} else if (unicode > view.getUint16(endOff + m2, false)) {
|
||||
lo = mid + 1;
|
||||
} else {
|
||||
var idR = view.getUint16(rangeOff + m2, false);
|
||||
if (idR === 0) {
|
||||
/* idDelta 用 getUint16 读(与原急切模式一致),依赖 % 0x10000 处理符号 */
|
||||
return (unicode + view.getUint16(deltaOff + m2, false)) % 0x10000;
|
||||
}
|
||||
var graphIdArrayIndexOffset = _graphIdArrayIndexOffset != null ? _graphIdArrayIndexOffset : (format4.glyphIdArrayOffset - format4.idRangeOffsetOffset) / 2;
|
||||
var index = mid + (idR >> 1) + (unicode - sCode) - graphIdArrayIndexOffset;
|
||||
var graphId = view.getUint16(format4.glyphIdArrayOffset + index * 2, false);
|
||||
if (graphId !== 0) {
|
||||
return (graphId + view.getUint16(deltaOff + m2, false)) % 0x10000;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
var startCode = format4.startCode;
|
||||
var endCode = format4.endCode;
|
||||
var idDelta = format4.idDelta;
|
||||
var idRangeOffset = format4.idRangeOffset;
|
||||
var segCount = format4.segCount || (format4.segCountX2 / 2);
|
||||
|
||||
var lo = 0, hi = segCount - 1;
|
||||
while (lo <= hi) {
|
||||
var mid = (lo + hi) >> 1;
|
||||
if (unicode < startCode[mid]) {
|
||||
hi = mid - 1;
|
||||
} else if (unicode > endCode[mid]) {
|
||||
lo = mid + 1;
|
||||
var lo2 = 0, hi2 = segCount - 1;
|
||||
while (lo2 <= hi2) {
|
||||
var mid2 = (lo2 + hi2) >> 1;
|
||||
if (unicode < startCode[mid2]) {
|
||||
hi2 = mid2 - 1;
|
||||
} else if (unicode > endCode[mid2]) {
|
||||
lo2 = mid2 + 1;
|
||||
} else {
|
||||
var i = mid;
|
||||
var i = mid2;
|
||||
if (idRangeOffset[i] === 0) {
|
||||
return (unicode + idDelta[i]) % 0x10000;
|
||||
}
|
||||
var graphIdArrayIndexOffset = _graphIdArrayIndexOffset != null ? _graphIdArrayIndexOffset : (format4.glyphIdArrayOffset - format4.idRangeOffsetOffset) / 2;
|
||||
var index = i + (idRangeOffset[i] >> 1) + (unicode - startCode[i]) - graphIdArrayIndexOffset;
|
||||
var graphId;
|
||||
var graphIdArrayIndexOffset2 = _graphIdArrayIndexOffset != null ? _graphIdArrayIndexOffset : (format4.glyphIdArrayOffset - format4.idRangeOffsetOffset) / 2;
|
||||
var index2 = i + (idRangeOffset[i] >> 1) + (unicode - startCode[i]) - graphIdArrayIndexOffset2;
|
||||
var graphId2;
|
||||
if (format4.glyphIdArray) {
|
||||
graphId = format4.glyphIdArray[index];
|
||||
graphId2 = format4.glyphIdArray[index2];
|
||||
} else if (format4._cmapView) {
|
||||
graphId = format4._cmapView.getUint16(format4.glyphIdArrayOffset + index * 2, false);
|
||||
graphId2 = format4._cmapView.getUint16(format4.glyphIdArrayOffset + index2 * 2, false);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
if (graphId !== 0) {
|
||||
return (graphId + idDelta[i]) % 0x10000;
|
||||
if (graphId2 !== 0) {
|
||||
return (graphId2 + idDelta[i]) % 0x10000;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user