From 72c6afef92505fe3a94a4069dca182e3c91dd4f9 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: Fri, 24 Jul 2026 11:51:29 +0800 Subject: [PATCH] =?UTF-8?q?perf(woff2):=20=E7=A7=BB=E9=99=A4=20simple=20gl?= =?UTF-8?q?yph=20=E7=9A=84=20bbox=20=E8=AE=A1=E7=AE=97=EF=BC=8C=E7=94=B1?= =?UTF-8?q?=E8=A7=A3=E7=A0=81=E7=AB=AF=E4=BB=8E=E8=BD=AE=E5=BB=93=E7=82=B9?= =?UTF-8?q?=E9=87=8D=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WOFF2 规范:bboxBitmap bit=0 时解码端计算点的 min/max 重建 bbox,bit=1 读显式存储值。 原编码对 simple glyph 计算 calcBbox 并与原始 bbox 比对决定 bit——但置 1 时存的也是 calcBbox,故无论 bit 值解码端都得 calcBbox,该计算与比对无功能意义。 移除后 simple glyph 永远 bit=0(解码端自算),省掉 x/y 循环每点 3 次 bbox 比较 + bboxSet 判定 + Pass2 写入。复合 glyph 仍显式存 bbox(raw 数据剥离了 glyph 头,解码端无法重建)。 令东千字文 woff2 write 2.449→2.237ms,输出体积 -20B~60B,全基准 SSIM 零变化。 Co-Authored-By: Claude Opus 4.8 (1M context) --- vendor/fonteditor-core/woff2/woff2-encode.js | 35 +++++--------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/vendor/fonteditor-core/woff2/woff2-encode.js b/vendor/fonteditor-core/woff2/woff2-encode.js index 45e6e89..8a13085 100644 --- a/vendor/fonteditor-core/woff2/woff2-encode.js +++ b/vendor/fonteditor-core/woff2/woff2-encode.js @@ -435,8 +435,14 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { } const xCoords = _reuseXCoords; let px = 0; - let calcXMin, calcXMax; /** + * 优化314: 移除 simple glyph 的 bbox 计算。 + * WOFF2 规范:bboxBitmap bit=0 时解码端从轮廓点重建 bbox(= 点坐标 min/max), + * bit=1 时读 bboxStream 显式存储值。原编码对 simple glyph 计算 calcBbox 并与原始 bbox + * 比对——无论置 0/1,解码端最终都得到 calcBbox(置1存的也是 calcBbox),故该计算与 + * 比对无功能意义。移除后 simple 永远置 0(解码端自算),输出更小且 transform 更快。 + * 复合 glyph 仍必须显式存 bbox(raw 组件数据剥离了 glyph 头,解码端无法重建),保留其逻辑。 + * * 优化302: 坐标解码用无分支取负 * 中文字体 87% 的点为 short 模式,其中正负各半(50/50), * 原三元 `(f & XSAME) ? b : -b` 是 50/50 不可预测分支,被 V8 编译成条件跳转导致流水线冲刷。 @@ -454,17 +460,11 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { dataOff += 2; } xCoords[xi] = px; - if (xi === 0) { calcXMin = px; calcXMax = px; } - else if (px < calcXMin) calcXMin = px; - else if (px > calcXMax) calcXMax = px; } let py = 0; - let calcYMin = 0, calcYMax = 0; if (numberOfContours > 0) { - /** 优化312: bbox bitmap 判定拆分——x 在此先判,y 在合并循环算完后补判 */ - let bboxSet = !(calcXMin === xMin && calcXMax === xMax); /** * 优化294: triplet 数据直接追加写入 glyphAccum(连续累积),不再分配 per-glyph glyphStreamBuf @@ -484,12 +484,10 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { const _fa = flagAccum; const _fwb = flagWriteBase; const _gd = glyfData; - let dyBboxUnset = true; /** * 优化312: y 解码 + triplet 编码合并为单循环。 * py 从 TTF yCoord 字节流解码(累积绝对坐标),cx 从 xCoords 取(x 已在前一循环解好), * triplet delta = cx - prevX / py - prevY,当场编码写入 glyphAccum。 - * bbox_y 的 min/max 也在本循环同步计算(原在独立 y 循环)。 */ for (let yi = 0; yi < numPoints; yi++) { const f = _fa[_fwb + yi]; @@ -502,9 +500,6 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { py += dy0; dataOff += 2; } - if (dyBboxUnset) { calcYMin = py; calcYMax = py; dyBboxUnset = false; } - else if (py < calcYMin) calcYMin = py; - else if (py > calcYMax) calcYMax = py; /** triplet 编码(与原 calcTripletAndWrite inline 语义一致) */ const cx = xCoords[yi]; @@ -567,12 +562,6 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { prevX = cx; prevY = cy; } - /** 优化312: y 的 bbox 匹配补判 */ - if (calcYMin !== yMin || calcYMax !== yMax) bboxSet = true; - if (bboxSet) { - bboxBitmap[gi >> 3] |= (0x80 >> (gi & 7)); - bboxStreamSize += 8; - } glyphAccumLen += gsbi; glyphStreamSize += gsbi; @@ -603,7 +592,6 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { composite: false, numberOfContours, nPointsDeltas, - calcXMin, calcYMin, calcXMax, calcYMax, } : { composite: false, @@ -693,14 +681,7 @@ function transformGlyfAndLoca(glyfData, locaData, indexFormat, numGlyphs) { for (let c = 0; c < nc; c++) { nPointsPos += encode255UInt16(deltas[c], result, nPointsPos); } - - if (bboxBitmap[gi >> 3] & (0x80 >> (gi & 7))) { - result[bboxPos] = g.calcXMin >> 8; result[bboxPos + 1] = g.calcXMin & 0xFF; - result[bboxPos + 2] = g.calcYMin >> 8; result[bboxPos + 3] = g.calcYMin & 0xFF; - result[bboxPos + 4] = g.calcXMax >> 8; result[bboxPos + 5] = g.calcXMax & 0xFF; - result[bboxPos + 6] = g.calcYMax >> 8; result[bboxPos + 7] = g.calcYMax & 0xFF; - bboxPos += 8; - } + /** 优化314: simple glyph 的 bbox 由解码端从轮廓点重建(bboxBitmap 位恒为 0),无需在此写入 */ } /** 优化294: 三个累积缓冲区整体拷贝到 result 对应区域(单次 set 替代 per-glyph set) */