编辑组那个素材导出小页面上有一根从 0 拉到 1 的质量滑杆。我一直默认 0.8 在哪个浏览器里都是同一个 0.8。这次我测了 Playwright 1.61.1 自带的三个内核。差得离谱。样本是我用脚本造的 2000×1500 合成图:渐变打底再叠上噪声、半透明色块和细线。存成 PNG 是 6,329,764 字节。JPEG 和 WebP 各跑 0 到 1.0 共 11 档。另外还加了 0.92、0.75 和一串怪值:1.5、-1、NaN、null、字符串 '0.8'。默认值落在哪一档我比的是文件的 sha256 而不是看体积像不像。WebP 那组另有一个对照,我用的是 图映 ImgIng 的格式转换,在 WebKit 26.5 上把同一张合成图按质量 80 转成 WebP。
toBlob 默认 quality 是多少:三个内核的清单
- JPEG:Chromium 149 和 Firefox 151 不传就是 0.92,产物与 0.92 档逐字节相同。
- JPEG:WebKit 26.5 不是 0.92。
- WebP:Chromium 默认 0.8,Firefox 默认 0.92。
- PNG:三家都不理 quality,传 0.1、传 1 和不传出来的文件一模一样。
WebKit 这个默认值我差点就写错了。不传时是 1,012,982 字节,0.75 档是 1,013,040 字节。只差 58 字节。光看体积我就要宣布「默认 0.75」了。后来我补扫了 0.700 到 0.800 之间每 0.005 一档,没有一档逐字节相等。能说的只有它的体积落在 0.745 和 0.75 两档之间。WebP 两家默认不同的后果更直观。同一张图不传 quality 时 Firefox 的 WebP 是 Chromium 的 2.56 倍(1,034,932 对 404,244 字节)。
quality 传字符串会怎样:等于没传
Chromium 里 0.8 出来是 446,131 字节,传字符串 '0.8' 拿到的却是 957,988 字节。它跟默认 0.92 那档一字不差,体积是预期的 2.15 倍。Firefox 更狠。它那边是 3.90 倍。越界值也不会被夹到边上。1.5 不会变成 1。-1 也不会变成 0。NaN 和 null 同样悄悄回到默认值。这个坑在滑杆场景里特别好踩,因为 range 输入框读出来的 value 本来就是字符串。我翻了下自己那个页面的第一版代码。还真是直接传进去的。
同一个 quality 为什么体积不一样
同样传数字 0.8,Chromium、Firefox、WebKit 分别出 446,131、473,089 和 1,126,619 字节。WebKit 是 Chromium 的 2.53 倍,它对这个数值的映射和另外两家不是一回事。它在 q=0 时还有 102,898 字节,大致相当于 Chromium 的 0.3 档。Chromium 和 Firefox 之间的差别藏得更深一点。q 不超过 0.8 时两家解码出来的像素逐字节相同,文件却不一样大。0.8 档 Firefox 大 6.0%,0 档大 108%。差别只在熵编码那一层。画面没区别。Firefox 还有一个台阶。它在 0.8 到 0.9 之间把色度采样从 4:2:0 换成了 4:4:4,体积一步跳了 3.08 倍。Chromium 到 0.92 还是 4:2:0,要到 1.0 才换。0.9 到 1.0 涨了 6.4 倍。
WebP 这边也有两处要留神。quality 填 1.0 在 Chromium 和 Firefox 上都会切到无损 VP8L,体积有 5.9 到 6.7 MB。Chromium 那份 6,654,054 字节比源 PNG 还大。WebKit 26.5 更干脆。它编不了 WebP。quality 填多少都给同一张 7,993,002 字节的 PNG。图映在这个内核上换成了自托管的 libwebp WASM,交出来的是 500,178 字节的真 WebP。PSNR 我也算了。它排不出谁的画质好:带噪声的样本让各档都挤在 28 到 33 dB 之间。WebKit 那几档分数更高只是因为它压得更轻而已。
质量滑杆怎么接才不踩坑
我现在的接法很简单。滑杆值先转成数字再自己夹到 0 到 1 之间。别指望浏览器兜底。不同浏览器之间不比 quality 数值,要控体积就直接看 blob.size 再决定要不要往下调一档。WebP 别给 1.0,除非你要的就是无损。Firefox 的 JPEG 在 0.9 附近会突然胖一截。这个台阶具体卡在哪一档我没细扫,只知道在 0.8 和 0.9 之间。