Save PNG compressed, WebP at effort 4 and AVIF at effort 1 (closes #232)
check / check (push) Waiting to run

Each output format now has its own govips export with its settings
named, in place of govips' generic Export, which sent libvips a zero for
some settings it was not given: PNG had no compression and WebP effort
0. PNG now gets libvips' default compression, 6, and WebP its default
effort, 4. GIF was already at libvips' default effort, and JPEG output
is unchanged.

AVIF was at libvips' default effort, 4, which takes minutes for an
8192x8192 image with one libvips thread, far past the default
downstream_timeout; effort 1 takes about 12 seconds. govips sends the
effort only with a bit depth, so AVIF is always 8 bits per sample: a
16-bit source no longer gets 12.

Model: opus-5-5
This commit is contained in:
2026-10-08 09:27:48 +00:00
parent 8597253ffd
commit 9ae1afa02e
3 changed files with 237 additions and 28 deletions
+83 -28
View File
@@ -504,36 +504,21 @@ func (p *ImageProcessor) encode(
}
}
var params vips.ExportParams
switch format {
case FormatJPEG:
params = vips.ExportParams{
Format: vips.ImageTypeJPEG,
Quality: quality,
}
return exportJPEG(img, quality)
case FormatPNG:
params = vips.ExportParams{
Format: vips.ImageTypePNG,
}
return exportPNG(img)
case FormatGIF:
params = vips.ExportParams{
Format: vips.ImageTypeGIF,
}
return exportGIF(img)
case FormatWebP:
params = vips.ExportParams{
Format: vips.ImageTypeWEBP,
Quality: quality,
}
return exportWebP(img, quality)
case FormatAVIF:
params = vips.ExportParams{
Format: vips.ImageTypeAVIF,
Quality: quality,
}
return exportAVIF(img, quality)
case FormatJXL:
return exportJXL(img, quality)
@@ -544,17 +529,87 @@ func (p *ImageProcessor) encode(
default:
return nil, fmt.Errorf("%w: %s", ErrUnsupportedOutputFormat, format)
}
}
// Drop EXIF, XMP, IPTC and the ICC profile. govips ignores this for
// GIF, which carries none of them.
params.StripMetadata = true
// govips sends libvips Go's zero value for some settings an export leaves
// out, such as no compression at all for PNG, so each export below sets
// every setting whose zero value is not what pixa wants. Stripping metadata
// drops EXIF, XMP, IPTC and the ICC profile.
output, _, err := img.Export(&params)
if err != nil {
return nil, err
}
// exportJPEG encodes img as JPEG at quality, without metadata. The settings
// it leaves out are at libvips' defaults.
func exportJPEG(img *vips.ImageRef, quality int) ([]byte, error) {
output, _, err := img.ExportJpeg(&vips.JpegExportParams{
StripMetadata: true,
Quality: quality,
})
return output, nil
return output, err
}
// pngCompression is libvips' default PNG compression, from 0 (none) to 9.
const pngCompression = 6
// exportPNG encodes img as PNG at libvips' default compression and row
// filter, without metadata.
func exportPNG(img *vips.ImageRef) ([]byte, error) {
output, _, err := img.ExportPng(&vips.PngExportParams{
StripMetadata: true,
Compression: pngCompression,
Filter: vips.PngFilterNone,
})
return output, err
}
// gifEffort is libvips' default GIF effort, from 1 to 10.
const gifEffort = 7
// exportGIF encodes img as GIF at libvips' default effort. govips cannot
// have libvips strip metadata from GIF, which carries none.
func exportGIF(img *vips.ImageRef) ([]byte, error) {
output, _, err := img.ExportGIF(&vips.GifExportParams{Effort: gifEffort})
return output, err
}
// webpEffort is libvips' default WebP effort, from 0 (fastest) to 6.
const webpEffort = 4
// exportWebP encodes img as lossy WebP at quality and libvips' default
// effort, without metadata.
func exportWebP(img *vips.ImageRef, quality int) ([]byte, error) {
output, _, err := img.ExportWebp(&vips.WebpExportParams{
StripMetadata: true,
Quality: quality,
ReductionEffort: webpEffort,
})
return output, err
}
// avifEffort is the AVIF effort, from 0 (fastest) to 9. With one thread, as
// pixad runs libvips, libvips' default, 4, takes minutes to save an
// 8192x8192 image, far past the default downstream_timeout of 60 seconds; 1
// takes about 12 seconds, and 2 nearly a minute.
const avifEffort = 1
// avifBitdepth is the AVIF bit depth. govips sends the effort only together
// with a bit depth. 8 is what libvips chooses for an 8-bit image; a 16-bit
// image, which libvips would save with 12 bits, gets 8 too.
const avifBitdepth = 8
// exportAVIF encodes img as lossy AVIF at quality, avifEffort and
// avifBitdepth, without metadata.
func exportAVIF(img *vips.ImageRef, quality int) ([]byte, error) {
output, _, err := img.ExportAvif(&vips.AvifExportParams{
StripMetadata: true,
Quality: quality,
Effort: avifEffort,
Bitdepth: avifBitdepth,
})
return output, err
}
// jxlResolution is the resolution every JPEG XL image is saved with, in