跳转到内容

PNG 压缩级别、行滤波器(Filter)与调色板量化(Palette)

复制本页

PNG 经常被误认为“只有无损、体积大且无法调优”。实际上,libvips 的 pngsave 同时提供两条优化路径:

  1. 真彩色无损压缩调优:通过调节 zlib Compression(0..9)与 PNG **行预测滤波器(Row Filter)**平衡编码 CPU 耗时与体积;
  2. 8-bit 索引调色板量化(Palette: true,类似 pngquant):将 32-bit RGBA 图像量化为最多 256 色的带半透明索引色板,体积通常比真彩色 PNG 减小 60% ~ 75%。
透明背景上的半透明圆环矢量测试图
输入:透明图形720 × 480,RGBA
缩小并导出为保留 Alpha 通道的 240×160 PNG 图像
输出:PNG240 × 160,4 通道 RGBA,35,763 字节
参数项 govips PngExportParams sharp .png(options) pyvips / vips CLI pngsave 调优建议
压缩级别 Compression(0..9,默认 6) compressionLevel(0..9,默认 6) compression=6 线上实时生成建议 4 ~ 6;静态资产离线打包可用 9
行预测滤波器 Filter(PngFilterNone / All 等) adaptiveFiltering(布尔值) filter="none" / "all" 扁平 UI 图标/调色板图用 None 更快更小;连续色调照片用 All
开启调色板量化 Palette: true palette: true palette=True / --palette 开启后转为索引色 PNG(需 libvips 编译了 libimagequant 或 quantizr)
调色板色彩数 / 位深 Bitdepth(1 / 2 / 4 / 8) colours(2..256) / bitdepth bitdepth(或 colours) 256 色对应 bitdepth=8;16 色小图标可用 bitdepth=4
量化质量与抖动 Quality(0..100)、Dither(0..1) quality、dither、effort Q=85、dither=1.0、effort=7 降低 Dither(如 0.5)可让大面积纯色块在 deflate 压缩中获得更高压缩率
隔行扫描(Adam7) Interlace: true progressive: true interlace=True 不推荐开启:Adam7 通常会使 PNG 体积增大 10%~20% 且解码更慢

1. 真彩色无损 PNG 与行滤波器选择

Section titled “1. 真彩色无损 PNG 与行滤波器选择”

PNG 在用 zlib 压缩前,会对每一行像素做差分预测(Sub、Up、Average、Paeth)。对于色块分明的扁平图标、二维码或线条图,禁用预测滤波(PngFilterNone / adaptiveFiltering: false)不仅编码速度提升数倍,输出体积往往还比自适应滤波更小:

params := vips.NewPngExportParams()
params.Compression = 6
params.Filter = vips.PngFilterNone
params.Interlace = false
params.StripMetadata = true
data, _, err := image.ExportPng(params)
if err != nil {
return err
}
return os.WriteFile("lossless.png", data, 0o644)

2. 8-bit 索引调色板量化(Palette PNG8)

Section titled “2. 8-bit 索引调色板量化(Palette PNG8)”

当业务必须输出 .png 格式(例如兼容不支持 WebP 的旧客户端或邮件模板),但又希望大幅压缩带半透明阴影的徽章、贴纸和 UI 资源时,可以启用 Palette: true:

params := vips.NewPngExportParams()
params.Palette = true
params.Bitdepth = 8 // 最多 256 色
params.Quality = 85 // 量化质量目标 (0..100)
params.Dither = 0.8 // Floyd-Steinberg 抖动强度 (0.0..1.0)
params.Compression = 6
params.Filter = vips.PngFilterNone
data, _, err := image.ExportPng(params)
if err != nil {
return err
}
return os.WriteFile("palette-8bit.png", data, 0o644)