From 32afa17b5cdd29e0b2889575d117514ebf00dd83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B4=AE=E7=94=9F=EF=BC=88=E5=AD=90=E8=99=9A=EF=BC=89?= <2234839456@qq.com> Date: Sat, 25 Jul 2026 03:54:54 +0800 Subject: [PATCH] =?UTF-8?q?perf(woff2):=20transformGlyfAndLoca=20x/y=20?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E7=94=A8=20TTF=20delta=20=E7=BC=96=E7=A0=81?= =?UTF-8?q?=20triplet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原 x 解码循环把 TTF delta(相对前点的有符号差)累积成绝对坐标 px, y+triplet 循环又用 cx-prevX / cy-prevY 把绝对坐标还原回 delta——无意义的 往返计算。因 TTF xCoordinates/yCoordinates 本就是 delta,triplet 的 dx/dy 语义 = 当前点与前点坐标差 = TTF delta 本身,可直接用: - x 循环存 delta 到 xCoords(省 px 累积) - y+triplet 循环 dx=xCoords[yi]、dy=解码出的 y delta(省 py 累积 + cx-prevX + cy-prevY + prevX/prevY 维护,每点省 4 次 ALU) case 分支逻辑不变(delta=0 ⟺ 绝对差=0),输出逐字节一致。 8 产物 A/B 全 IDENTICAL(含 ttf/otf 路径)。性能 best-of min: 千字文 woff2 -2.4%(1.56→1.52ms)、令东8字 -2.8%、FiraCode -1.1%。 基准测试全 SSIM 通过。 Co-Authored-By: Claude Opus 4.8 (1M context) --- vendor/fonteditor-core/woff2/woff2-encode.js | 39 ++++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/vendor/fonteditor-core/woff2/woff2-encode.js b/vendor/fonteditor-core/woff2/woff2-encode.js index 44ec0cd..45aa510 100644 --- a/vendor/fonteditor-core/woff2/woff2-encode.js +++ b/vendor/fonteditor-core/woff2/woff2-encode.js @@ -434,7 +434,6 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { _reuseXCoords = new Int32Array(newCap); } const xCoords = _reuseXCoords; - let px = 0; /** * 优化314: 移除 simple glyph 的 bbox 计算。 * WOFF2 规范:bboxBitmap bit=0 时解码端从轮廓点重建 bbox(= 点坐标 min/max), @@ -451,19 +450,20 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { for (let xi = 0; xi < numPoints; xi++) { const f = flagAccum[flagWriteBase + xi]; if (f & XSHORT_FLAG) { - const b = glyfData[dataOff++]; - px += b * (((f >> 4) & 1) * 2 - 1); + /** 存 delta(TTF xCoordinates 本就是相对前点的 delta),y+triplet 循环直接用, + * 省去原先 px 累积成绝对坐标、再用 cx-prevX 还原回 delta 的无意义往返 */ + xCoords[xi] = glyfData[dataOff++] * (((f >> 4) & 1) * 2 - 1); } else if (!(f & XSAME_FLAG)) { let dx = (glyfData[dataOff] << 8) | glyfData[dataOff + 1]; if (dx > 0x7FFF) dx -= 0x10000; - px += dx; + xCoords[xi] = dx; dataOff += 2; + } else { + /** XSAME_FLAG:该点 x 与前点相同,delta = 0 */ + xCoords[xi] = 0; } - xCoords[xi] = px; } - let py = 0; - if (numberOfContours > 0) { /** @@ -479,34 +479,35 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { } const gsBase = glyphAccumLen; let gsbi = 0; - let prevX = 0, prevY = 0; const _gs = glyphAccum; const _fa = flagAccum; const _fwb = flagWriteBase; const _gd = glyfData; /** - * 优化312: y 解码 + triplet 编码合并为单循环。 - * py 从 TTF yCoord 字节流解码(累积绝对坐标),cx 从 xCoords 取(x 已在前一循环解好), - * triplet delta = cx - prevX / py - prevY,当场编码写入 glyphAccum。 + * 优化318: x/y 都直接用 TTF delta 编码 triplet,省去绝对坐标累积+还原往返。 + * TTF xCoordinates/yCoordinates 本就是「相对前一点的有符号 delta」, + * triplet 的 dx/dy 语义 = 当前点与前点的坐标差 = TTF delta 本身。 + * 原 x 循环 px 累积成绝对、y 循环 py 累积成绝对再用 cx-prevX/cy-prevY 还原回 delta 是无意义往返。 + * 现 x 循环直接存 delta(xCoords),本循环 dy 直接取解码出的 y delta、dx 直接取 xCoords[yi]。 */ for (let yi = 0; yi < numPoints; yi++) { const f = _fa[_fwb + yi]; + let dy; if (f & YSHORT_FLAG) { - const b = _gd[dataOff++]; - py += b * (((f >> 5) & 1) * 2 - 1); + dy = _gd[dataOff++] * (((f >> 5) & 1) * 2 - 1); } else if (!(f & YSAME_FLAG)) { let dy0 = (_gd[dataOff] << 8) | _gd[dataOff + 1]; if (dy0 > 0x7FFF) dy0 -= 0x10000; - py += dy0; + dy = dy0; dataOff += 2; + } else { + /** YSAME_FLAG:该点 y 与前点相同,delta = 0 */ + dy = 0; } /** triplet 编码(与原 calcTripletAndWrite inline 语义一致) */ - const cx = xCoords[yi]; - const cy = py; + const dx = xCoords[yi]; const curveBit = ((f & 1) ^ 1) << 7; - const dx = cx - prevX; - const dy = cy - prevY; /** * 优化317: 无分支 abs + 符号位提取。 * dx/dy 为 SmI(Int32 路径),dx >> 31 在非负时为 0、负时为 -1(算术右移)。 @@ -567,8 +568,6 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { /** triplet flag 回写到 flagAccum(flagStream 存 triplet flag 而非原始 flag) */ _fa[_fwb + yi] = flag; gsbi += adv; - prevX = cx; - prevY = cy; } glyphAccumLen += gsbi; glyphStreamSize += gsbi;