mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-03-17 20:17:48 +02:00
Implement Max Bytes Filter (#275)
* Implement Max Bytes Filter * Update according to code review comments * Refactor according to code review comments
This commit is contained in:
parent
49aaecf464
commit
d74c402066
@ -169,6 +169,19 @@ Redefines quality of the resulting image, percentage.
|
||||
|
||||
Default: value from the environment variable.
|
||||
|
||||
#### Max Bytes
|
||||
|
||||
```
|
||||
max_bytes:%max_bytes
|
||||
mb:%max_bytes
|
||||
```
|
||||
|
||||
This filter automatically degrades the quality of the image until the image is under the specified amount of bytes.
|
||||
|
||||
*Warning: this filter processes image multiple times to achieve specified image size*
|
||||
|
||||
Default: 0
|
||||
|
||||
#### Background
|
||||
|
||||
```
|
||||
|
39
process.go
39
process.go
@ -133,6 +133,15 @@ func canScaleOnLoad(imgtype imageType, scale float64) bool {
|
||||
return imgtype == imageTypeJPEG || imgtype == imageTypeWEBP
|
||||
}
|
||||
|
||||
func canFitToBytes(imgtype imageType) bool {
|
||||
switch imgtype {
|
||||
case imageTypeJPEG, imageTypeWEBP, imageTypeHEIC, imageTypeTIFF:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func calcJpegShink(scale float64, imgtype imageType) int {
|
||||
shrink := int(1.0 / scale)
|
||||
|
||||
@ -682,5 +691,35 @@ func processImage(ctx context.Context) ([]byte, context.CancelFunc, error) {
|
||||
checkTimeout(ctx)
|
||||
}
|
||||
|
||||
if po.MaxBytes > 0 && canFitToBytes(po.Format) {
|
||||
return processToFitBytes(po, img)
|
||||
}
|
||||
|
||||
return img.Save(po.Format, po.Quality)
|
||||
}
|
||||
|
||||
func processToFitBytes(po *processingOptions, img *vipsImage) ([]byte, context.CancelFunc, error) {
|
||||
var diff float64
|
||||
quality := po.Quality
|
||||
|
||||
img.CopyMemory()
|
||||
|
||||
for {
|
||||
result, cancel, err := img.Save(po.Format, quality)
|
||||
if len(result) <= po.MaxBytes || quality <= 10 || err != nil {
|
||||
return result, cancel, err
|
||||
}
|
||||
cancel()
|
||||
|
||||
delta := float64(len(result)) / float64(po.MaxBytes)
|
||||
switch {
|
||||
case delta > 3:
|
||||
diff = 0.25
|
||||
case delta > 1.5:
|
||||
diff = 0.5
|
||||
default:
|
||||
diff = 0.75
|
||||
}
|
||||
quality = int(float64(quality) * diff)
|
||||
}
|
||||
}
|
||||
|
@ -116,6 +116,7 @@ type processingOptions struct {
|
||||
Crop cropOptions
|
||||
Format imageType
|
||||
Quality int
|
||||
MaxBytes int
|
||||
Flatten bool
|
||||
Background rgbColor
|
||||
Blur float32
|
||||
@ -193,6 +194,7 @@ func newProcessingOptions() *processingOptions {
|
||||
Gravity: gravityOptions{Type: gravityCenter},
|
||||
Enlarge: false,
|
||||
Quality: conf.Quality,
|
||||
MaxBytes: 0,
|
||||
Format: imageTypeUnknown,
|
||||
Background: rgbColor{255, 255, 255},
|
||||
Blur: 0,
|
||||
@ -542,6 +544,20 @@ func applyQualityOption(po *processingOptions, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyMaxBytesOption(po *processingOptions, args []string) error {
|
||||
if len(args) > 1 {
|
||||
return fmt.Errorf("Invalid max_bytes arguments: %v", args)
|
||||
}
|
||||
|
||||
if max, err := strconv.Atoi(args[0]); err == nil && max >= 0 {
|
||||
po.MaxBytes = max
|
||||
} else {
|
||||
return fmt.Errorf("Invalid max_bytes: %s", args[0])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyBackgroundOption(po *processingOptions, args []string) error {
|
||||
switch len(args) {
|
||||
case 1:
|
||||
@ -744,6 +760,8 @@ func applyProcessingOption(po *processingOptions, name string, args []string) er
|
||||
return applyCropOption(po, args)
|
||||
case "quality", "q":
|
||||
return applyQualityOption(po, args)
|
||||
case "max_bytes", "mb":
|
||||
return applyMaxBytesOption(po, args)
|
||||
case "background", "bg":
|
||||
return applyBackgroundOption(po, args)
|
||||
case "blur", "bl":
|
||||
|
Loading…
x
Reference in New Issue
Block a user