Skip to content
Permalink
Browse files

Implement Max Bytes Filter (#275)

* Implement Max Bytes Filter

* Update according to code review comments

* Refactor according to code review comments
  • Loading branch information
dmitryzuev authored and DarthSim committed Nov 29, 2019
1 parent 49aaecf commit d74c402066eba2c185fa6c8eb89b887635916fd5
Showing with 70 additions and 0 deletions.
  1. +13 −0 docs/generating_the_url_advanced.md
  2. +39 −0 process.go
  3. +18 −0 processing_options.go
@@ -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

```
@@ -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":

0 comments on commit d74c402

Please sign in to comment.
You can’t perform that action at this time.