Browse Source

Fix sizes of GIF and ICO before save

DarthSim 2 years ago
parent
commit
97e15c717d
6 changed files with 110 additions and 36 deletions
  1. 87 0
      processing/fix_size.go
  2. 0 33
      processing/fix_webp_size.go
  3. 1 1
      processing/processing.go
  4. 13 0
      vips/vips.c
  5. 8 0
      vips/vips.go
  6. 1 2
      vips/vips.h

+ 87 - 0
processing/fix_size.go

@@ -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
+}

+ 0 - 33
processing/fix_webp_size.go

@@ -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()
-}

+ 1 - 1
processing/processing.go

@@ -27,10 +27,10 @@ var mainPipeline = pipeline{
 	scale,
 	rotateAndFlip,
 	cropToResult,
-	fixWebpSize,
 	applyFilters,
 	extend,
 	padding,
+	fixSize,
 	flatten,
 	watermark,
 	exportColorProfile,

+ 13 - 0
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)

+ 8 - 0
vips/vips.go

@@ -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)

+ 1 - 2
vips/vips.h

@@ -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);