Skip to content

PNG compression levels, row filters, and palette quantization

Copy page

PNG is often treated as an inflexible lossless format, yet libvips pngsave provides two distinct optimization levers:

  1. Truecolour lossless tuning: balancing zlib Compression (0..9) and PNG row prediction filters (PngFilter) for speed vs size;
  2. 8-bit indexed palette quantization (Palette: true, similar to pngquant): quantizing 32-bit RGBA graphics into a 256-colour RGBA palette, typically shrinking UI icons, badges, and stickers by 60%–75%.
Translucent ring vector graphic on a transparent background
Input: transparent graphic720 × 480, RGBA
240×160 PNG output with preserved alpha transparency
Output: PNG240 × 160, 4-band RGBA, 35,763 bytes
Setting govips PngExportParams sharp .png(options) pyvips / vips CLI pngsave Tuning guidance
Compression level Compression (0..9, default 6) compressionLevel (0..9, default 6) compression=6 4–6 for real-time HTTP pipelines; 9 for offline static asset builds
Row prediction filter Filter (PngFilterNone / All) adaptiveFiltering (boolean) filter="none" / "all" None is faster and smaller on flat UI/palette graphics; All suits continuous-tone photos
Indexed palette mode Palette: true palette: true palette=True / --palette Converts to indexed PNG8 (requires libimagequant or quantizr in libvips)
Palette colours / bitdepth Bitdepth (1 / 2 / 4 / 8) colours (2..256) / bitdepth bitdepth (or colours) bitdepth=8 for up to 256 colours; bitdepth=4 for 16-colour icons
Quantization quality & dither Quality (0..100), Dither (0..1) quality, dither, effort Q=85, dither=0.8, effort=7 Lowering Dither slightly improves deflate compression on flat colour fills
Adam7 interlacing Interlace: true progressive: true interlace=True Avoid in production: Adam7 inflates PNG size by 10%–20% and slows decoding

1. Truecolour lossless PNG and row filter selection

Section titled “1. Truecolour lossless PNG and row filter selection”

Before zlib compression runs, PNG applies a scanline prediction filter (None, Sub, Up, Average, Paeth). On flat UI graphics, QR codes, line art, or indexed images, disabling adaptive filtering (PngFilterNone / adaptiveFiltering: false) encodes several times faster and often yields a smaller file:

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 indexed palette quantization (PNG8 with full alpha)

Section titled “2. 8-bit indexed palette quantization (PNG8 with full alpha)”

When .png output is required (for legacy clients or email templates) and you want to shrink translucent badges, stickers, or UI overlays dramatically, enable Palette: true:

params := vips.NewPngExportParams()
params.Palette = true
params.Bitdepth = 8 // Up to 256 RGBA palette entries
params.Quality = 85 // Quantization quality target (0..100)
params.Dither = 0.8 // Floyd-Steinberg dithering strength (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)