mirror of
				https://github.com/imgproxy/imgproxy.git
				synced 2025-10-30 23:08:02 +02:00 
			
		
		
		
	Fix sizes of GIF and ICO before save
This commit is contained in:
		
							
								
								
									
										87
									
								
								processing/fix_size.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								processing/fix_size.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| package processing | ||||
|  | ||||
| import ( | ||||
| 	"math" | ||||
|  | ||||
| 	"github.com/imgproxy/imgproxy/v3/imagedata" | ||||
| 	"github.com/imgproxy/imgproxy/v3/imagetype" | ||||
| 	"github.com/imgproxy/imgproxy/v3/imath" | ||||
| 	"github.com/imgproxy/imgproxy/v3/options" | ||||
| 	"github.com/imgproxy/imgproxy/v3/vips" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// https://chromium.googlesource.com/webm/libwebp/+/refs/heads/master/src/webp/encode.h#529 | ||||
| 	webpMaxDimension = 16383.0 | ||||
| 	gifMaxDimension  = 65535.0 | ||||
| 	icoMaxDimension  = 256.0 | ||||
| ) | ||||
|  | ||||
| func fixWebpSize(img *vips.Image) error { | ||||
| 	webpLimitShrink := float64(imath.Max(img.Width(), img.Height())) / webpMaxDimension | ||||
|  | ||||
| 	if webpLimitShrink <= 1.0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	scale := 1.0 / webpLimitShrink | ||||
| 	if err := img.Resize(scale, scale); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	log.Warningf("WebP dimension size is limited to %d. The image is rescaled to %dx%d", int(webpMaxDimension), img.Width(), img.Height()) | ||||
|  | ||||
| 	return img.CopyMemory() | ||||
| } | ||||
|  | ||||
| func fixGifSize(img *vips.Image) error { | ||||
| 	gifMaxResolution := float64(vips.GifResolutionLimit()) | ||||
| 	gifResLimitShrink := float64(img.Width()*img.Height()) / gifMaxResolution | ||||
| 	gifDimLimitShrink := float64(imath.Max(img.Width(), img.Height())) / gifMaxDimension | ||||
|  | ||||
| 	gifLimitShrink := math.Max(gifResLimitShrink, gifDimLimitShrink) | ||||
|  | ||||
| 	if gifLimitShrink <= 1.0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	scale := math.Sqrt(1.0 / gifLimitShrink) | ||||
| 	if err := img.Resize(scale, scale); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	log.Warningf("GIF resolution is limited to %d and dimension size is limited to %d. The image is rescaled to %dx%d", int(gifMaxResolution), int(gifMaxDimension), img.Width(), img.Height()) | ||||
|  | ||||
| 	return img.CopyMemory() | ||||
| } | ||||
|  | ||||
| func fixIcoSize(img *vips.Image) error { | ||||
| 	icoLimitShrink := float64(imath.Max(img.Width(), img.Height())) / icoMaxDimension | ||||
|  | ||||
| 	if icoLimitShrink <= 1.0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	scale := 1.0 / icoLimitShrink | ||||
| 	if err := img.Resize(scale, scale); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	log.Warningf("ICO dimension size is limited to %d. The image is rescaled to %dx%d", int(icoMaxDimension), img.Width(), img.Height()) | ||||
|  | ||||
| 	return img.CopyMemory() | ||||
| } | ||||
|  | ||||
| func fixSize(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error { | ||||
| 	switch po.Format { | ||||
| 	case imagetype.WEBP: | ||||
| 		return fixWebpSize(img) | ||||
| 	case imagetype.GIF: | ||||
| 		return fixGifSize(img) | ||||
| 	case imagetype.ICO: | ||||
| 		return fixIcoSize(img) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @@ -1,33 +0,0 @@ | ||||
| package processing | ||||
|  | ||||
| import ( | ||||
| 	"github.com/imgproxy/imgproxy/v3/imagedata" | ||||
| 	"github.com/imgproxy/imgproxy/v3/imagetype" | ||||
| 	"github.com/imgproxy/imgproxy/v3/imath" | ||||
| 	"github.com/imgproxy/imgproxy/v3/options" | ||||
| 	"github.com/imgproxy/imgproxy/v3/vips" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| // https://chromium.googlesource.com/webm/libwebp/+/refs/heads/master/src/webp/encode.h#529 | ||||
| const webpMaxDimension = 16383.0 | ||||
|  | ||||
| func fixWebpSize(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error { | ||||
| 	if po.Format != imagetype.WEBP { | ||||
| 		return nil | ||||
| 	} | ||||
| 	webpLimitShrink := float64(imath.Max(img.Width(), img.Height())) / webpMaxDimension | ||||
|  | ||||
| 	if webpLimitShrink <= 1.0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	scale := 1.0 / webpLimitShrink | ||||
| 	if err := img.Resize(scale, scale); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	log.Warningf("WebP dimension size is limited to %d. The image is rescaled to %dx%d", int(webpMaxDimension), img.Width(), img.Height()) | ||||
|  | ||||
| 	return img.CopyMemory() | ||||
| } | ||||
| @@ -27,10 +27,10 @@ var mainPipeline = pipeline{ | ||||
| 	scale, | ||||
| 	rotateAndFlip, | ||||
| 	cropToResult, | ||||
| 	fixWebpSize, | ||||
| 	applyFilters, | ||||
| 	extend, | ||||
| 	padding, | ||||
| 	fixSize, | ||||
| 	flatten, | ||||
| 	watermark, | ||||
| 	exportColorProfile, | ||||
|   | ||||
							
								
								
									
										13
									
								
								vips/vips.c
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								vips/vips.c
									
									
									
									
									
								
							| @@ -12,6 +12,9 @@ | ||||
| #define VIPS_SUPPORT_GIFSAVE \ | ||||
|   (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 12)) | ||||
|  | ||||
| #define VIPS_GIF_RESOLUTION_LIMITED \ | ||||
|   (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION <= 12) | ||||
|  | ||||
| int | ||||
| vips_initialize() { | ||||
|   return vips_init("imgproxy"); | ||||
| @@ -33,6 +36,16 @@ swap_and_clear(VipsImage **in, VipsImage *out) { | ||||
|   *in = out; | ||||
| } | ||||
|  | ||||
| int | ||||
| gif_resolution_limit() { | ||||
| #if VIPS_GIF_RESOLUTION_LIMITED | ||||
|   // https://github.com/libvips/libvips/blob/v8.12.2/libvips/foreign/cgifsave.c#L437-L442 | ||||
|   return 2000 * 2000; | ||||
| #else | ||||
|   return INT_MAX / 4; | ||||
| #endif | ||||
| } | ||||
|  | ||||
| int | ||||
| vips_jpegload_go(void *buf, size_t len, int shrink, VipsImage **out) { | ||||
|   if (shrink > 1) | ||||
|   | ||||
| @@ -31,6 +31,8 @@ type Image struct { | ||||
| var ( | ||||
| 	typeSupportLoad sync.Map | ||||
| 	typeSupportSave sync.Map | ||||
|  | ||||
| 	gifResolutionLimit int | ||||
| ) | ||||
|  | ||||
| var vipsConf struct { | ||||
| @@ -69,6 +71,8 @@ func Init() error { | ||||
| 		C.vips_cache_set_trace(C.gboolean(1)) | ||||
| 	} | ||||
|  | ||||
| 	gifResolutionLimit = int(C.gif_resolution_limit()) | ||||
|  | ||||
| 	vipsConf.JpegProgressive = gbool(config.JpegProgressive) | ||||
| 	vipsConf.PngInterlaced = gbool(config.PngInterlaced) | ||||
| 	vipsConf.PngQuantize = gbool(config.PngQuantize) | ||||
| @@ -183,6 +187,10 @@ func SupportsSave(it imagetype.Type) bool { | ||||
| 	return sup | ||||
| } | ||||
|  | ||||
| func GifResolutionLimit() int { | ||||
| 	return gifResolutionLimit | ||||
| } | ||||
|  | ||||
| func gbool(b bool) C.gboolean { | ||||
| 	if b { | ||||
| 		return C.gboolean(1) | ||||
|   | ||||
| @@ -11,8 +11,7 @@ void g_free_go(void **buf); | ||||
|  | ||||
| void swap_and_clear(VipsImage **in, VipsImage *out); | ||||
|  | ||||
| int vips_type_find_load_go(int imgtype); | ||||
| int vips_type_find_save_go(int imgtype); | ||||
| int gif_resolution_limit(); | ||||
|  | ||||
| int vips_jpegload_go(void *buf, size_t len, int shrink, VipsImage **out); | ||||
| int vips_pngload_go(void *buf, size_t len, VipsImage **out); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user