mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-10-09 19:52:30 +02:00
IMGPROXY_PREFERRED_FORMATS config
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
- Add support of 16-bit BMP.
|
- Add support of 16-bit BMP.
|
||||||
- Add `IMGPROXY_NEW_RELIC_LABELS` config.
|
- Add `IMGPROXY_NEW_RELIC_LABELS` config.
|
||||||
- Add support of JPEG files with differential Huffman coding or arithmetic coding.
|
- Add support of JPEG files with differential Huffman coding or arithmetic coding.
|
||||||
|
- Add `IMGPROXY_PREFERRED_FORMATS` config.
|
||||||
|
|
||||||
### Fix
|
### Fix
|
||||||
- Fix trimming of CMYK images.
|
- Fix trimming of CMYK images.
|
||||||
|
@@ -59,6 +59,8 @@ var (
|
|||||||
EnforceAvif bool
|
EnforceAvif bool
|
||||||
EnableClientHints bool
|
EnableClientHints bool
|
||||||
|
|
||||||
|
PreferredFormats []imagetype.Type
|
||||||
|
|
||||||
SkipProcessingFormats []imagetype.Type
|
SkipProcessingFormats []imagetype.Type
|
||||||
|
|
||||||
UseLinearColorspace bool
|
UseLinearColorspace bool
|
||||||
@@ -220,6 +222,15 @@ func Reset() {
|
|||||||
EnforceAvif = false
|
EnforceAvif = false
|
||||||
EnableClientHints = false
|
EnableClientHints = false
|
||||||
|
|
||||||
|
PreferredFormats = []imagetype.Type{
|
||||||
|
imagetype.JPEG,
|
||||||
|
imagetype.PNG,
|
||||||
|
imagetype.GIF,
|
||||||
|
imagetype.WEBP,
|
||||||
|
imagetype.AVIF,
|
||||||
|
imagetype.ICO,
|
||||||
|
}
|
||||||
|
|
||||||
SkipProcessingFormats = make([]imagetype.Type, 0)
|
SkipProcessingFormats = make([]imagetype.Type, 0)
|
||||||
|
|
||||||
UseLinearColorspace = false
|
UseLinearColorspace = false
|
||||||
@@ -376,6 +387,10 @@ func Configure() error {
|
|||||||
|
|
||||||
configurators.String(&HealthCheckPath, "IMGPROXY_HEALTH_CHECK_PATH")
|
configurators.String(&HealthCheckPath, "IMGPROXY_HEALTH_CHECK_PATH")
|
||||||
|
|
||||||
|
if err := configurators.ImageTypes(&PreferredFormats, "IMGPROXY_PREFERRED_FORMATS"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := configurators.ImageTypes(&SkipProcessingFormats, "IMGPROXY_SKIP_PROCESSING_FORMATS"); err != nil {
|
if err := configurators.ImageTypes(&SkipProcessingFormats, "IMGPROXY_SKIP_PROCESSING_FORMATS"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -557,6 +572,10 @@ func Configure() error {
|
|||||||
return fmt.Errorf("Quality can't be greater than 100, now - %d\n", Quality)
|
return fmt.Errorf("Quality can't be greater than 100, now - %d\n", Quality)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(PreferredFormats) == 0 {
|
||||||
|
return fmt.Errorf("At least one preferred format should be specified")
|
||||||
|
}
|
||||||
|
|
||||||
if IgnoreSslVerification {
|
if IgnoreSslVerification {
|
||||||
log.Warning("Ignoring SSL verification is very unsafe")
|
log.Warning("Ignoring SSL verification is very unsafe")
|
||||||
}
|
}
|
||||||
|
@@ -103,17 +103,17 @@ func Bool(b *bool, name string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ImageTypes(it *[]imagetype.Type, name string) error {
|
func ImageTypes(it *[]imagetype.Type, name string) error {
|
||||||
*it = []imagetype.Type{}
|
|
||||||
|
|
||||||
if env := os.Getenv(name); len(env) > 0 {
|
if env := os.Getenv(name); len(env) > 0 {
|
||||||
parts := strings.Split(env, ",")
|
parts := strings.Split(env, ",")
|
||||||
|
|
||||||
|
*it = make([]imagetype.Type, 0, len(parts))
|
||||||
|
|
||||||
for _, p := range parts {
|
for _, p := range parts {
|
||||||
pt := strings.TrimSpace(p)
|
pt := strings.TrimSpace(p)
|
||||||
if t, ok := imagetype.Types[pt]; ok {
|
if t, ok := imagetype.Types[pt]; ok {
|
||||||
*it = append(*it, t)
|
*it = append(*it, t)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("Unknown image format to skip: %s", pt)
|
return fmt.Errorf("Unknown image format: %s", pt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -249,6 +249,22 @@ You can set up a fallback image that will be used in case imgproxy is unable to
|
|||||||
* `IMGPROXY_FALLBACK_IMAGE_TTL`: a duration (in seconds) sent via the `Expires` and `Cache-Control: max-age` HTTP headers when a fallback image was used. When blank or `0`, the value from `IMGPROXY_TTL` is used.
|
* `IMGPROXY_FALLBACK_IMAGE_TTL`: a duration (in seconds) sent via the `Expires` and `Cache-Control: max-age` HTTP headers when a fallback image was used. When blank or `0`, the value from `IMGPROXY_TTL` is used.
|
||||||
* `IMGPROXY_FALLBACK_IMAGES_CACHE_SIZE`:  the size of custom fallback images cache. When set to `0`, the fallback image cache is disabled. 256 fallback images are cached by default.
|
* `IMGPROXY_FALLBACK_IMAGES_CACHE_SIZE`:  the size of custom fallback images cache. When set to `0`, the fallback image cache is disabled. 256 fallback images are cached by default.
|
||||||
|
|
||||||
|
## Preferred formats
|
||||||
|
|
||||||
|
When the resulting image format is not explicitly specified in the imgproxy URL via the extension or the `format` processing option, imgproxy will choose one of the preferred formats as the resultant:
|
||||||
|
|
||||||
|
* `IMGPROXY_PREFERRED_FORMATS`: a list of preferred formats, comma divided. Default: `jpeg,png,gif,webp,avif,ico`
|
||||||
|
|
||||||
|
imgproxy is guided by the following rules when choosing the resulting format:
|
||||||
|
|
||||||
|
1. If the preferred formats list contains the source image format, it will be used as the resultant
|
||||||
|
2. If the resulting image is animated, the resulting image format should support animations
|
||||||
|
3. If the resulting image contains transparency, the resulting image format should support transparency
|
||||||
|
4. imgproxy chooses the first preferred format that meets those requirements
|
||||||
|
5. If none of the preferred formats meet the requirements, the first preferred format is used as the resultant
|
||||||
|
|
||||||
|
**📝Note:** When AVIF/WebP support detection is enabled and the browser supports AVIF/WebP, it may be used as the resultant format even if the preferred formats list doesn't contain it.
|
||||||
|
|
||||||
## Skip processing
|
## Skip processing
|
||||||
|
|
||||||
You can configure imgproxy to skip processing of some formats:
|
You can configure imgproxy to skip processing of some formats:
|
||||||
|
6
main.go
6
main.go
@@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/imgproxy/imgproxy/v3/metrics"
|
"github.com/imgproxy/imgproxy/v3/metrics"
|
||||||
"github.com/imgproxy/imgproxy/v3/metrics/prometheus"
|
"github.com/imgproxy/imgproxy/v3/metrics/prometheus"
|
||||||
"github.com/imgproxy/imgproxy/v3/options"
|
"github.com/imgproxy/imgproxy/v3/options"
|
||||||
|
"github.com/imgproxy/imgproxy/v3/processing"
|
||||||
"github.com/imgproxy/imgproxy/v3/version"
|
"github.com/imgproxy/imgproxy/v3/version"
|
||||||
"github.com/imgproxy/imgproxy/v3/vips"
|
"github.com/imgproxy/imgproxy/v3/vips"
|
||||||
)
|
)
|
||||||
@@ -51,6 +52,11 @@ func initialize() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := processing.ValidatePreferredFormats(); err != nil {
|
||||||
|
vips.Shutdown()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := options.ParsePresets(config.Presets); err != nil {
|
if err := options.ParsePresets(config.Presets); err != nil {
|
||||||
vips.Shutdown()
|
vips.Shutdown()
|
||||||
return err
|
return err
|
||||||
|
@@ -37,9 +37,40 @@ var mainPipeline = pipeline{
|
|||||||
finalize,
|
finalize,
|
||||||
}
|
}
|
||||||
|
|
||||||
func imageTypeGoodForWeb(imgtype imagetype.Type) bool {
|
func isImageTypePreferred(imgtype imagetype.Type) bool {
|
||||||
return imgtype != imagetype.TIFF &&
|
for _, t := range config.PreferredFormats {
|
||||||
imgtype != imagetype.BMP
|
if imgtype == t {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func findBestFormat(srcType imagetype.Type, animated, expectAlpha bool) imagetype.Type {
|
||||||
|
for _, t := range config.PreferredFormats {
|
||||||
|
if animated && !t.SupportsAnimation() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if expectAlpha && !t.SupportsAlpha() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
return config.PreferredFormats[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidatePreferredFormats() error {
|
||||||
|
for _, t := range config.PreferredFormats {
|
||||||
|
if !vips.SupportsSave(t) {
|
||||||
|
return fmt.Errorf("%s can't be a preferred format as it's saving is not supported", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func canFitToBytes(imgtype imagetype.Type) bool {
|
func canFitToBytes(imgtype imagetype.Type) bool {
|
||||||
@@ -223,6 +254,7 @@ func ProcessImage(ctx context.Context, imgdata *imagedata.ImageData, po *options
|
|||||||
originWidth, originHeight := getImageSize(img)
|
originWidth, originHeight := getImageSize(img)
|
||||||
|
|
||||||
animated := img.IsAnimated()
|
animated := img.IsAnimated()
|
||||||
|
expectAlpha := !po.Flatten && (img.HasAlpha() || po.Padding.Enabled || po.Extend.Enabled)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case po.Format == imagetype.Unknown:
|
case po.Format == imagetype.Unknown:
|
||||||
@@ -231,10 +263,10 @@ func ProcessImage(ctx context.Context, imgdata *imagedata.ImageData, po *options
|
|||||||
po.Format = imagetype.AVIF
|
po.Format = imagetype.AVIF
|
||||||
case po.PreferWebP:
|
case po.PreferWebP:
|
||||||
po.Format = imagetype.WEBP
|
po.Format = imagetype.WEBP
|
||||||
case vips.SupportsSave(imgdata.Type) && imageTypeGoodForWeb(imgdata.Type):
|
case isImageTypePreferred(imgdata.Type):
|
||||||
po.Format = imgdata.Type
|
po.Format = imgdata.Type
|
||||||
default:
|
default:
|
||||||
po.Format = imagetype.JPEG
|
po.Format = findBestFormat(imgdata.Type, animated, expectAlpha)
|
||||||
}
|
}
|
||||||
case po.EnforceAvif && !animated:
|
case po.EnforceAvif && !animated:
|
||||||
po.Format = imagetype.AVIF
|
po.Format = imagetype.AVIF
|
||||||
|
Reference in New Issue
Block a user