1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374 |
- 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))
- }
- }
|