Browse Source

PNG quantization

DarthSim 6 years ago
parent
commit
4ba970bdf6
6 changed files with 46 additions and 13 deletions
  1. 2 2
      Dockerfile
  2. 15 4
      config.go
  3. 2 0
      docs/configuration.md
  4. 12 4
      process.go
  5. 14 2
      vips.c
  6. 1 1
      vips.h

+ 2 - 2
Dockerfile

@@ -11,7 +11,7 @@ WORKDIR /go/src/github.com/DarthSim/imgproxy
 RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
 RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
   && apk --no-cache upgrade \
   && apk --no-cache upgrade \
   && apk add --no-cache curl ca-certificates go gcc g++ make musl-dev fftw-dev glib-dev expat-dev \
   && apk add --no-cache curl ca-certificates go gcc g++ make musl-dev fftw-dev glib-dev expat-dev \
-    libjpeg-turbo-dev libpng-dev libwebp-dev giflib-dev librsvg-dev libexif-dev lcms2-dev
+    libjpeg-turbo-dev libpng-dev libwebp-dev giflib-dev librsvg-dev libexif-dev lcms2-dev libimagequant-dev
 
 
 # Build ImageMagick
 # Build ImageMagick
 RUN cd /root \
 RUN cd /root \
@@ -76,7 +76,7 @@ LABEL maintainer="Sergey Alexandrovich <darthsim@gmail.com>"
 RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
 RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
   && apk --no-cache upgrade \
   && apk --no-cache upgrade \
   && apk add --no-cache bash ca-certificates fftw glib expat libjpeg-turbo libpng \
   && apk add --no-cache bash ca-certificates fftw glib expat libjpeg-turbo libpng \
-    libwebp giflib librsvg libgsf libexif lcms2 \
+    libwebp giflib librsvg libgsf libexif lcms2 libimagequant\
   && rm -rf /var/cache/apk*
   && rm -rf /var/cache/apk*
 
 
 COPY --from=0 /usr/local/bin/imgproxy /usr/local/bin/
 COPY --from=0 /usr/local/bin/imgproxy /usr/local/bin/

+ 15 - 4
config.go

@@ -143,10 +143,12 @@ type config struct {
 	MaxSrcFileSize   int
 	MaxSrcFileSize   int
 	MaxGifFrames     int
 	MaxGifFrames     int
 
 
-	JpegProgressive bool
-	PngInterlaced   bool
-	Quality         int
-	GZipCompression int
+	JpegProgressive       bool
+	PngInterlaced         bool
+	PngQuantize           bool
+	PngQuantizationColors int
+	Quality               int
+	GZipCompression       int
 
 
 	EnableWebpDetection bool
 	EnableWebpDetection bool
 	EnforceWebp         bool
 	EnforceWebp         bool
@@ -215,6 +217,7 @@ var conf = config{
 	MaxGifFrames:                   1,
 	MaxGifFrames:                   1,
 	AllowInsecure:                  false,
 	AllowInsecure:                  false,
 	SignatureSize:                  32,
 	SignatureSize:                  32,
+	PngQuantizationColors:          256,
 	Quality:                        80,
 	Quality:                        80,
 	GZipCompression:                5,
 	GZipCompression:                5,
 	UserAgent:                      fmt.Sprintf("imgproxy/%s", version),
 	UserAgent:                      fmt.Sprintf("imgproxy/%s", version),
@@ -263,6 +266,8 @@ func init() {
 
 
 	boolEnvConfig(&conf.JpegProgressive, "IMGPROXY_JPEG_PROGRESSIVE")
 	boolEnvConfig(&conf.JpegProgressive, "IMGPROXY_JPEG_PROGRESSIVE")
 	boolEnvConfig(&conf.PngInterlaced, "IMGPROXY_PNG_INTERLACED")
 	boolEnvConfig(&conf.PngInterlaced, "IMGPROXY_PNG_INTERLACED")
+	boolEnvConfig(&conf.PngQuantize, "IMGPROXY_PNG_QUANTIZE")
+	intEnvConfig(&conf.PngQuantizationColors, "IMGPROXY_PNG_QUANTIZATION_COLORS")
 	intEnvConfig(&conf.Quality, "IMGPROXY_QUALITY")
 	intEnvConfig(&conf.Quality, "IMGPROXY_QUALITY")
 	intEnvConfig(&conf.GZipCompression, "IMGPROXY_GZIP_COMPRESSION")
 	intEnvConfig(&conf.GZipCompression, "IMGPROXY_GZIP_COMPRESSION")
 
 
@@ -388,6 +393,12 @@ func init() {
 		logFatal("Max GIF frames should be greater than 0, now - %d\n", conf.MaxGifFrames)
 		logFatal("Max GIF frames should be greater than 0, now - %d\n", conf.MaxGifFrames)
 	}
 	}
 
 
+	if conf.PngQuantizationColors < 2 {
+		logFatal("Png quantization colors should be greater than 1, now - %d\n", conf.PngQuantizationColors)
+	} else if conf.PngQuantizationColors > 256 {
+		logFatal("Png quantization colors can't be greater than 256, now - %d\n", conf.PngQuantizationColors)
+	}
+
 	if conf.Quality <= 0 {
 	if conf.Quality <= 0 {
 		logFatal("Quality should be greater than 0, now - %d\n", conf.Quality)
 		logFatal("Quality should be greater than 0, now - %d\n", conf.Quality)
 	} else if conf.Quality > 100 {
 	} else if conf.Quality > 100 {

+ 2 - 0
docs/configuration.md

@@ -67,6 +67,8 @@ When you use imgproxy in a development environment, it can be useful to ignore S
 * `IMGPROXY_GZIP_COMPRESSION`: GZip compression level. Default: `5`;
 * `IMGPROXY_GZIP_COMPRESSION`: GZip compression level. Default: `5`;
 * `IMGPROXY_JPEG_PROGRESSIVE` : when true, enables progressive JPEG compression. Default: false;
 * `IMGPROXY_JPEG_PROGRESSIVE` : when true, enables progressive JPEG compression. Default: false;
 * `IMGPROXY_PNG_INTERLACED`: when true, enables interlaced PNG compression. Default: false;
 * `IMGPROXY_PNG_INTERLACED`: when true, enables interlaced PNG compression. Default: false;
+* `IMGPROXY_PNG_QUANTIZE`: when true, enables PNG quantization. libvips should be built with libimagequant support. Default: false;
+* `IMGPROXY_PNG_QUANTIZATION_COLORS`: maximum number of quantization palette entries. Should be between 2 and 256. Default: 256;
 
 
 ## WebP support detection
 ## WebP support detection
 
 

+ 12 - 4
process.go

@@ -31,9 +31,11 @@ var (
 )
 )
 
 
 type cConfig struct {
 type cConfig struct {
-	JpegProgressive  C.int
-	PngInterlaced    C.int
-	WatermarkOpacity C.double
+	JpegProgressive       C.int
+	PngInterlaced         C.int
+	PngQuantize           C.int
+	PngQuantizationColors C.int
+	WatermarkOpacity      C.double
 }
 }
 
 
 var cConf cConfig
 var cConf cConfig
@@ -109,6 +111,12 @@ func initVips() {
 		cConf.PngInterlaced = C.int(1)
 		cConf.PngInterlaced = C.int(1)
 	}
 	}
 
 
+	if conf.PngQuantize {
+		cConf.PngQuantize = C.int(1)
+	}
+
+	cConf.PngQuantizationColors = C.int(conf.PngQuantizationColors)
+
 	cConf.WatermarkOpacity = C.double(conf.WatermarkOpacity)
 	cConf.WatermarkOpacity = C.double(conf.WatermarkOpacity)
 
 
 	if err := vipsPrepareWatermark(); err != nil {
 	if err := vipsPrepareWatermark(); err != nil {
@@ -667,7 +675,7 @@ func vipsSaveImage(img *C.VipsImage, imgtype imageType, quality int) ([]byte, co
 	case imageTypeJPEG:
 	case imageTypeJPEG:
 		err = C.vips_jpegsave_go(img, &ptr, &imgsize, C.int(quality), cConf.JpegProgressive)
 		err = C.vips_jpegsave_go(img, &ptr, &imgsize, C.int(quality), cConf.JpegProgressive)
 	case imageTypePNG:
 	case imageTypePNG:
-		err = C.vips_pngsave_go(img, &ptr, &imgsize, cConf.PngInterlaced)
+		err = C.vips_pngsave_go(img, &ptr, &imgsize, cConf.PngInterlaced, cConf.PngQuantize, cConf.PngQuantizationColors)
 	case imageTypeWEBP:
 	case imageTypeWEBP:
 		err = C.vips_webpsave_go(img, &ptr, &imgsize, C.int(quality))
 		err = C.vips_webpsave_go(img, &ptr, &imgsize, C.int(quality))
 	case imageTypeGIF:
 	case imageTypeGIF:

+ 14 - 2
vips.c

@@ -15,6 +15,9 @@
 #define VIPS_SUPPORT_MAGICK \
 #define VIPS_SUPPORT_MAGICK \
   (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 7))
   (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 7))
 
 
+#define VIPS_SUPPORT_PNG_QUANTIZATION \
+  (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 7))
+
 #define EXIF_ORIENTATION "exif-ifd0-Orientation"
 #define EXIF_ORIENTATION "exif-ifd0-Orientation"
 
 
 int
 int
@@ -456,8 +459,17 @@ vips_jpegsave_go(VipsImage *in, void **buf, size_t *len, int quality, int interl
 }
 }
 
 
 int
 int
-vips_pngsave_go(VipsImage *in, void **buf, size_t *len, int interlace) {
-  return vips_pngsave_buffer(in, buf, len, "profile", "none", "filter", VIPS_FOREIGN_PNG_FILTER_NONE, "interlace", interlace, NULL);
+vips_pngsave_go(VipsImage *in, void **buf, size_t *len, int interlace, int quantize, int colors) {
+  return vips_pngsave_buffer(
+    in, buf, len,
+    "profile", "none",
+    "filter", VIPS_FOREIGN_PNG_FILTER_NONE,
+    "interlace", interlace,
+#if VIPS_SUPPORT_PNG_QUANTIZATION
+    "palette", quantize,
+    "colours", colors,
+#endif // VIPS_SUPPORT_PNG_QUANTIZATION
+    NULL);
 }
 }
 
 
 int
 int

+ 1 - 1
vips.h

@@ -73,7 +73,7 @@ int vips_apply_watermark(VipsImage *in, VipsImage *watermark, VipsImage **out, d
 int vips_arrayjoin_go(VipsImage **in, VipsImage **out, int n);
 int vips_arrayjoin_go(VipsImage **in, VipsImage **out, int n);
 
 
 int vips_jpegsave_go(VipsImage *in, void **buf, size_t *len, int quality, int interlace);
 int vips_jpegsave_go(VipsImage *in, void **buf, size_t *len, int quality, int interlace);
-int vips_pngsave_go(VipsImage *in, void **buf, size_t *len, int interlace);
+int vips_pngsave_go(VipsImage *in, void **buf, size_t *len, int interlace, int quantize, int colors);
 int vips_webpsave_go(VipsImage *in, void **buf, size_t *len, int quality);
 int vips_webpsave_go(VipsImage *in, void **buf, size_t *len, int quality);
 int vips_gifsave_go(VipsImage *in, void **buf, size_t *len);
 int vips_gifsave_go(VipsImage *in, void **buf, size_t *len);
 int vips_icosave_go(VipsImage *in, void **buf, size_t *len);
 int vips_icosave_go(VipsImage *in, void **buf, size_t *len);