雜記 › 技術筆記 › 位圖進 SVG 只是換了個盒子:232 字節的容器常數、一個從不生效的「base64 嵌入」勾選,與四檔縮放下紋絲不動的 142064 個字元

位圖進 SVG 只是換了個盒子:232 字節的容器常數、一個從不生效的「base64 嵌入」勾選,與四檔縮放下紋絲不動的 142064 個字元

這一頁只做封裝不做描摹:容器開銷恆為 232 字節、「base64 嵌入」勾選三態產物逐字節不變、四檔縮放下 142064 個字元一動不動,再加上下載 URL 那層的第二次膨脹,逐筆量過。

先把結論釘在字節上

這一頁的產物裡沒有 <path>,只有 <image> 那一行把整條 data URL 原樣抱住。全模板 grep canvas、drawImage、toDataURL、trace、quantiz、posterize 各 0 次,位圖入口只有 readAsDataURL 的 1 次(L144)。28 張自造圖加上同批另外 3 張共 31 個檔案,取回 base64 解碼後與輸入逐字節 31/31 全等。

受測的是圖片轉 SVG那頁的模板,10270 字節,腳本段逐字摘進 node:vm 原樣跑。

容器開銷是一個常數

buildSvg(L157–L198)的固定部分是 15 段字面量相加:39+40+48+12+10+2+18+1+3+16+10+2+21+4+6 = 232 字節,含 7 枚換行、27 枚縮排空格。於是 svgB = 232 + 3*len(w) + 3*len(h) + prefixLen + b64Len:w、h 各寫三遍,每個數字字元付 3 字節;prefixLen 只有 22 與 23。31 行容器恆等式表的 resid 全 0,例 n=581743 的 jpg:775660 + 21 + 23 + 232 = 775936。

base64 只回報核對結果

ceil(n/3)*4 命中 31/31;補位分三檔,餘 0 時尾下 0 枚(28 張裡 8 張)、餘 1 補 2 枚(12 張)、餘 2 補 1 枚(8 張)。整除樣本的 b64/n 正好 1.333333,偏離只在幾十上百字節的量級看得見:a_1x1.png 是 1.369863,到 100 KB 級只剩 0.000025。

從不生效的「base64 嵌入」

L21 那枚 checkbox 全文出現 1 次、出廠 checked,腳本區塊裡按 id 取它 0 次。對 d_101x67.png 把勾選切成三態各建置一次,三次產物的 sha 前 12 位都是 5d037f0d8a4b、長度都是 30249,取消勾選後輸出逐字節不變。L22 那句「使用 base64 嵌入(產生獨立 SVG 檔案)」在這一頁沒有落點,因為這一頁唯一的嵌入方式就是 base64。

原圖 B → base64 字元 → SVG B a_1x1.png 73 100 360 urlLen/svgB 1.6694 h_1200x900.png 1030404 1373872 1374147 整條 1.3336 1.1440 28 張合計 3808141 5085119 1.3353 下界 1.0552 屬於 n_123x45.bmp

圖 1:同一條鏈上的三層長度。

四檔縮放只改三個聲明數字

L159/L160 的 Math.round 只產出 6 個數字槽,L168 的 base64 與檔位無關。那張 24 行的縮放表裡,241×137 的 b64Len 恆為 142064,聲明依次是 60×34、121×69、241×137、482×274;101×67 的 PNG 恆為 29980、BMP 恆為 27232。取整痕跡 120.5 → 121、60.25 → 60;1×1 選 25% 歸零成 width="0" height="0" 與 image_0x0.svg。

檔位聲明 w×hsvgB介面「SVG 大小」
25%60×34142330139.0 KB
50%121×69142333139.0 KB
100%241×137142336139.0 KB
200%482×274142336139.0 KB

檔位之間那 6 字節的差只來自位數變化,位圖數據一動不動。

標籤會停在舊檔位

出廠的三處口徑互不相同:L26 的 active 在 25%、L85 的預設 imgSvgScale=100、檔位標籤出廠寫 100%。上傳後 L131–L134 重置 scale=100、把 active 移到下標 2,卻不碰標籤文字——全文給這個 id 賦值的只有 L152。實測先點 25% 再上傳另一張,標籤停在 "25%"、實際 imgSvgScale=100、聲明與固有都是 241×137,也就是按 100% 出圖而介面寫著 25%。

下載 URL 的第二次膨脹

L171 的 encodeURIComponent 只動 base64 裡的加號、斜線、等號,每枚從 1 個字元變成 3 個字元。合式的 urlLen = 33 + svgB + 2*esc 在那張 18 行的表上全 OK,骨架固定貢獻 100 枚被轉義字元。urlLen/svgB 從 1.6694 收斂到 1.0552,大檔案穩定在 1.055 到 1.072;h_1200x900.png 因斜線佔 4.5669%、加號佔 2.6251% 被推到 1.1440。預覽與下載吃的是這一層,介面顯示的是上一層。

顯示口徑與格式判定

formatSize(L226–L230)先判檔再 toFixed(1),於是 1048575 顯示成 1024.0 KB,h_1200x900.png 相鄰兩行一個是 1006.3 KB、一個是 1.3 MB——這是本頁的顯示口徑,不是檔案系統差異。

L103 那個正則 /image\// 沒有錨點、沒有 i 標誌,17 個 type 放行 11 條、攔截 6 條:攔下空字串、text/plain、application/pdf、IMAGE/PNG、png、image,而 ximage/png、foo/image/bar、not/image/、text/html,image/png 全過,「格式」行顯示成 XPNG、FOO/BAR、NOT/、TEXT/HTML,PNG。grep onerror 0 次,解碼失敗時整段不執行、面板不亮。

動圖與文案對照

j_anim2.gif(900 字節、loop=0)兩幀延遲 20cs 與 50cs,封裝後解回字節、重解幀結構逐欄位相同,MIME 仍是 image/gif,svgB 1472。0 尺寸 SVG 的渲染、Chrome 同名追加 (1)、data URL 長度上限、6.4 MB 進 innerHTML 的代價連同逐幀播放都未實測,原因是 Node 沒有渲染與下載堆疊;JPG 與 WEBP 的字節流由 headless Chrome 代編碼並回核過前綴與長度。文案依 config/tools.php 第 46 個條目判 17 句:一致 6、需限定與部分一致 4、衝突 7,其中 5 句指著同一處程式碼事實。

封裝與描摹的分界,舊文位圖轉向量寫過,不復述;容器之間的搬運歸 SVG 格式轉換,本文對它沒有量測。

常見問題

Q:取消「base64 嵌入」勾選,產物會變嗎?

不會,三態產物的 sha 前 12 位與長度 30249 全同。

Q:選 25% 之後檔案會小一點嗎?

241×137 四檔的 b64Len 恆為 142064,svgB 只差 6 字節,介面都是 139.0 KB。

Q:GIF 動圖封進去保得住嗎?

字節與幀結構保得住,延遲 20cs 與 50cs 反解後逐欄位相同;播放體驗未實測。

📎 相關文章

拖到 4 和拖到 8,螢幕上都是 43890 個塊:馬賽克預覽與導出的兩套塊網格、三檔會崩與 6px 誤觸閾值 2026-11-10 轉完悄悄少了 271 個字符:Markdown 轉 HTML 的洞協議、64 輪回填與 20 條提示上限 2026-11-10 解碼是四步連做:1194 條語料在 unicode-convert 頁上只有 798 條能原樣回來 2026-11-09 保留集縮成 66 個字元:這一頁三檔編碼在 128 個碼位上的逐字元實測與 9 格編解碼矩陣 2026-11-09 介面上八個框的先後和程式碼執行順序是反的:文字清理頁硬編碼八步順序、逐對換序真值表與統計碼元口徑實測 2026-11-09