mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-10-03 18:05:50 +02:00
75 lines
2.0 KiB
Go
75 lines
2.0 KiB
Go
package processing
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/imgproxy/imgproxy/v3/imagedata"
|
|
"github.com/imgproxy/imgproxy/v3/imath"
|
|
"github.com/imgproxy/imgproxy/v3/options"
|
|
"github.com/imgproxy/imgproxy/v3/server"
|
|
"github.com/imgproxy/imgproxy/v3/vips"
|
|
)
|
|
|
|
// saveImageToFitBytes tries to save the image to fit into the specified max bytes
|
|
// by lowering the quality. It returns the image data that fits the requirement
|
|
// or the best effort data if it was not possible to fit into the limit.
|
|
func saveImageToFitBytes(
|
|
ctx context.Context,
|
|
po *options.ProcessingOptions,
|
|
img *vips.Image,
|
|
) (imagedata.ImageData, error) {
|
|
var newQuality int
|
|
|
|
// Start with the quality specified in the options.
|
|
quality := po.GetQuality()
|
|
|
|
// We will probably save the image multiple times, so we need to process its pixels
|
|
// to ensure that it is in random access mode.
|
|
if err := img.CopyMemory(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for {
|
|
// Check for timeout or cancellation before each attempt as we might spend too much
|
|
// time processing the image or making previous attempts.
|
|
if err := server.CheckTimeout(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
imgdata, err := img.Save(po.Format, quality)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
size, err := imgdata.Size()
|
|
if err != nil {
|
|
imgdata.Close()
|
|
return nil, err
|
|
}
|
|
|
|
// If we fit the limit or quality is too low, return the result.
|
|
if size <= po.MaxBytes || quality <= 10 {
|
|
return imgdata, err
|
|
}
|
|
|
|
// We don't need the image data anymore, close it to free resources.
|
|
imgdata.Close()
|
|
|
|
// Tune quality for the next attempt based on how much we exceed the limit.
|
|
delta := float64(size) / float64(po.MaxBytes)
|
|
switch {
|
|
case delta > 3:
|
|
newQuality = imath.Scale(quality, 0.25)
|
|
case delta > 1.5:
|
|
newQuality = imath.Scale(quality, 0.5)
|
|
default:
|
|
newQuality = imath.Scale(quality, 0.75)
|
|
}
|
|
|
|
// Ensure that quality is always lowered, even if the scaling
|
|
// doesn't change it due to rounding.
|
|
// Also, ensure that quality doesn't go below the minimum.
|
|
quality = max(1, min(quality-1, newQuality))
|
|
}
|
|
}
|