塊數相同、畫面相同,導出差 4 倍
標題那個 43890 的輸入:Node 合成的 1080×2400 PNG(只印虛構單號 DEMO-A)、clientWidth=764、innerHeight=800,預覽畫布 dw×dh=252×560、scale=0.233333,框 4 個選區批次跑。滑桿拖到 4 與拖到 8,塊數都是 43890,取樣點 (20,30) 恆為 [147,123,161];導出側是 201613 塊與 50468 塊。
被測頁圖片馬賽克 488 行 / 20006 位元組。兩套塊寬公式:預覽 Math.max(2, mosaicSize * scale)(L224,小數、按預覽像素),導出 Math.max(2, mosaicSize)(L449,整數、按原圖像素),中間夾 applyMosaic 入口 L261–262 的四處 Math.floor。
鉗制只認一條判據
scale < 2/mosaicSize 就鉗:本頁 scale=0.233333,滑桿 4 需 <0.5、6 需 <0.333333、8 需 <0.25 全落進去,16 需 <0.125 就夠不到。兩個分母別混——8 圖 × 13 檔 = 104 組裡 21 組被鉗,8 圖 × 15 檔 = 120 組裡 26 組(21.7%)。maxH 在 innerHeight ≥ 760 恆為 560,於是原圖高 > 1120px 配滑桿 4 必鉗。
| 圖 | 被鉗檔 | 預覽等效 M |
|---|---|---|
| 1080×2400 | 4…8(5 檔) | 8.571px |
| 1170×2532 | 4…9(6 檔) | 9.043px |
| 4032×3024 | 4…10(7 檔) | 10.800px |
| 1080×4000 | 4…14(11 檔) | 14.286px |
| 1080×1080 | 0 檔 | M 恆等於 N |
四枚預設裡只有「細緻」6 會被吃掉,導出側仍按 6px。窄螢幕 clientWidth=298 上 4032×3024 的 scale 掉到 0.072917,「細緻」與「中等」同時鉗成同一個 bs=2.0000、M 都是 27.429。
圖 1:同一檔位上灰柱矮於描邊柱。
三檔會崩,被鉗的反而不崩
真模板殼頁 HeadlessChrome/156,圖 1080×2400、dw=252、region (10,20,110,24):mosaicSize=10、12、20 三檔拋 IndexSizeError(source width is 0,N=20 報的是 source height is 0),其餘檔位正常。根因是 getImageData 四參數按 WebIDL long 向零截斷,bw 的小數尾巴 0.33333333333341386、0.8000000000000824、0.6666666666666714 分別有 12、9、1 處截成 0。
換一條選區就可能不崩:(60,90,140,210) 在 N=10、12 上截斷處數都是 0;4/6/8 更因 bs 釘成整數 2 而完全不崩。模板 try/catch 計數 0,且 L165 先寫文案、L166 才重繪:字樣已經變成 20px、畫面停在上一檔網格。
四處 floor 與殘列
(10,20,110,24) 換算到原圖是 42.857,85.714,471.429,102.857,floor 成 42,85,471,102,右邊界淨誤差 −1.286px、下邊界 −1.571px;同圖 (60,90,140,210) → 257,385,600,900、(100,20,60,40) → 428,85,257,171。
網格從選區左上角起算,末列常不滿:bs=16 時這條選區是 7 列 2 行 14 塊、殘列寬 14;固定 bs=16 掃選區寬 1…200,非零殘列平均 6.851px。
6px 判在預覽像素上
tr.w >= 6 && tr.h >= 6(L365)比的是預覽像素,原圖最小可框高度 = 6/scale:1080×1080 為 11.571px、1080×2400 為 25.714px、4032×3024 為 32.4px、1080×4000 為 42.857px。
按單行文字建模:1080×2400 上原圖行高 18px 預覽只有 4.2、24px 只有 5.6,兩檔靜默丟棄不給提示,30px → 7 才收得下;4032×3024 的 30px → 5.556 仍丟,要 40px → 7.407。
alpha 與 RGB 同權平均
四通道同權相加(L281–285),L286 把 a/255 拼進 fillStyle,source-over 疊在已畫好原圖的畫布上(L222、L443):a=128 殘留 0.498039、a=64 殘留 0.749020、a=0 殘留 1.000000。128 在串裡寫成 0.5019607843137255,逐通道 Math.round 最大偏差 0.5,單通道 0.196%。
「導出原圖」是一次重編碼
0 個選區點下載走「導出原圖」分支,產物解碼回來與原圖逐通道比對,4665600 個值 diffChannels=0、maxChannelDelta=0,位元組卻從 913850 變 163971、只剩 17.94%——像素相同、位元組不同。
toBlob(mime, 0.92) 實測:1080×2400 加 1 個選區 png 70882 / jpeg 320379 / webp 29190;1080×1080 帶 alpha 圖 png 167624 / jpeg 300531 / webp 245724。「JPG(體積更小)」在這兩張噪聲圖上都是反的(4.520 倍、1.793 倍),jpeg 還先在 L442 鋪 #ffffff 白底、把 alpha 抹成 255。
算術層與瀏覽器層分開標
塊網格取整、殘列寬、floor 偏移、clamp 臨界、alpha 合成出自純 Node 算術模型,已與 Chrome 156 逐檔 15/15 一致;三檔 IndexSizeError 與抗鋸齒(3.5px 寬塊邊緣 alpha=64/32 級)是瀏覽器實測。未實測:真實照片位元組分布、drawImage 重採樣視覺差異、toBlob 返回 null、下載耗時。要遮哪些欄位見站內售後復盤。
常見問題
Q:滑桿動了畫面為什麼不動?
落進 scale < 2/mosaicSize 時預覽塊寬被釘成 2,標稱 4、6、8 跑同一套網格,導出才按標稱走。
Q:拖了框卻沒打上碼?
預覽裡不足 6px 的選區被 L365 當誤觸丟棄;拖到 w=190 h=280 時切走視窗,blur 與 touchcancel 只置 isDragging=false、不 push,regions 仍為 0 且沒有提示。