浏览代码

Load ICO with vips_magickload

DarthSim 5 年之前
父节点
当前提交
4d06d397fe
共有 9 个文件被更改,包括 58 次插入50 次删除
  1. 0 1
      docker/Dockerfile
  2. 1 1
      docs/image_formats_support.md
  3. 0 1
      go.mod
  4. 0 2
      go.sum
  5. 0 28
      ico_data.go
  6. 34 10
      image_size/ico.go
  7. 12 0
      vips.c
  8. 10 7
      vips.go
  9. 1 0
      vips.h

+ 0 - 1
docker/Dockerfile

@@ -47,7 +47,6 @@ RUN cd /root \
   && curl -Ls https://github.com/libvips/libvips/releases/download/v$VIPS_VERSION/vips-$VIPS_VERSION.tar.gz | tar -xz \
   && cd vips-$VIPS_VERSION \
   && ./configure \
-    --disable-magickload \
     --without-python \
     --without-tiff \
     --without-OpenEXR \

+ 1 - 1
docs/image_formats_support.md

@@ -17,7 +17,7 @@ imgproxy supports GIF output only when using libvips 8.7.0+ compiled with ImageM
 
 ## ICO support
 
-imgproxy supports ICO output only when using libvips 8.7.0+ compiled with ImageMagick support. Official imgproxy Docker image supports ICO out of the box.
+imgproxy supports ICO only when using libvips 8.7.0+ compiled with ImageMagick support. Official imgproxy Docker image supports ICO out of the box.
 
 ## HEIC support
 

+ 0 - 1
go.mod

@@ -19,7 +19,6 @@ require (
 	github.com/honeybadger-io/honeybadger-go v0.4.0
 	github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 // indirect
 	github.com/kr/pretty v0.1.0 // indirect
-	github.com/mat/besticon v3.9.0+incompatible
 	github.com/matoous/go-nanoid v0.0.0-20181114085210-eab626deece6
 	github.com/newrelic/go-agent v2.2.0+incompatible
 	github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 // indirect

+ 0 - 2
go.sum

@@ -68,8 +68,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/mat/besticon v3.9.0+incompatible h1:SLaWKCE7ptsjWbQee8Sbx8F/WK4bw8b55tUV4mY0m/c=
-github.com/mat/besticon v3.9.0+incompatible/go.mod h1:mA1auQYHt6CW5e7L9HJLmqVQC8SzNk2gVwouO0AbiEU=
 github.com/matoous/go-nanoid v0.0.0-20181114085210-eab626deece6 h1:elv0VC5n2owefO3RvtKvS3i3/rw9LCrs0+tVFLbBlmQ=
 github.com/matoous/go-nanoid v0.0.0-20181114085210-eab626deece6/go.mod h1:soqXi4beH2aAljcVvgIDqekDtnM2UZkGl47fniwq3J4=
 github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=

+ 0 - 28
ico_data.go

@@ -1,28 +0,0 @@
-package main
-
-import (
-	"bytes"
-	"image"
-	"image/draw"
-
-	_ "github.com/mat/besticon/ico"
-)
-
-func icoData(data []byte) (out []byte, width int, height int, err error) {
-	var ico image.Image
-
-	ico, _, err = image.Decode(bytes.NewReader(data))
-	if err != nil {
-		return
-	}
-
-	// Ensure that image is in RGBA format
-	rgba := image.NewRGBA(ico.Bounds())
-	draw.Draw(rgba, ico.Bounds(), ico, image.Point{}, draw.Src)
-
-	width = rgba.Bounds().Dx()
-	height = rgba.Bounds().Dy()
-	out = rgba.Pix
-
-	return
-}

+ 34 - 10
image_size/ico.go

@@ -5,32 +5,56 @@ import (
 	"io"
 )
 
-func DecodeIcoMeta(r io.Reader) (*Meta, error) {
+func icoBestSize(r io.Reader) (width, height byte, page uint16, err error) {
 	var tmp [16]byte
 
-	if _, err := io.ReadFull(r, tmp[:6]); err != nil {
-		return nil, err
+	if _, err = io.ReadFull(r, tmp[:6]); err != nil {
+		return
 	}
 
 	count := binary.LittleEndian.Uint16(tmp[4:6])
 
-	width, height := byte(0), byte(0)
-
 	for i := uint16(0); i < count; i++ {
-		if _, err := io.ReadFull(r, tmp[:]); err != nil {
-			return nil, err
+		if _, err = io.ReadFull(r, tmp[:]); err != nil {
+			return
 		}
 
-		if tmp[0] > width || tmp[1] > height {
+		if tmp[0] > width || tmp[1] > height || tmp[0] == 0 || tmp[1] == 0 {
 			width = tmp[0]
 			height = tmp[1]
+			page = i
 		}
 	}
 
+	return
+}
+
+func BestIcoPage(r io.Reader) (int, error) {
+	_, _, page, err := icoBestSize(r)
+	return int(page), err
+}
+
+func DecodeIcoMeta(r io.Reader) (*Meta, error) {
+	bwidth, bheight, _, err := icoBestSize(r)
+	if err != nil {
+		return nil, err
+	}
+
+	width := int(bwidth)
+	height := int(bheight)
+
+	if width == 0 {
+		width = 256
+	}
+
+	if height == 0 {
+		height = 256
+	}
+
 	return &Meta{
 		Format: "ico",
-		Width:  int(width),
-		Height: int(height),
+		Width:  width,
+		Height: height,
 	}, nil
 }
 

+ 12 - 0
vips.c

@@ -80,6 +80,8 @@ vips_type_find_load_go(int imgtype) {
     return vips_type_find("VipsOperation", "gifload_buffer");
   case (SVG):
     return vips_type_find("VipsOperation", "svgload_buffer");
+  case (ICO):
+    return vips_type_find("VipsOperation", "magickload_buffer");
   case (HEIC):
     return vips_type_find("VipsOperation", "heifload_buffer");
   case (TIFF):
@@ -161,6 +163,16 @@ vips_svgload_go(void *buf, size_t len, double scale, VipsImage **out) {
   #endif
 }
 
+int
+vips_icoload_go(void *buf, size_t len, int page, VipsImage **out) {
+  #if VIPS_SUPPORT_MAGICK
+    return vips_magickload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, "page", page, NULL);
+  #else
+    vips_error("vips_icoload_go", "Loading ICO is not supported (libvips 8.7+ reuired)");
+    return 1;
+  #endif
+}
+
 int
 vips_heifload_go(void *buf, size_t len, VipsImage **out) {
 #if VIPS_SUPPORT_HEIF

+ 10 - 7
vips.go

@@ -8,12 +8,15 @@ package main
 */
 import "C"
 import (
+	"bytes"
 	"context"
 	"math"
 	"os"
 	"runtime"
 	"time"
 	"unsafe"
+
+	imageSize "github.com/imgproxy/imgproxy/image_size"
 )
 
 type vipsImage struct {
@@ -88,6 +91,9 @@ func initVips() {
 	if int(C.vips_type_find_load_go(C.int(imageTypeSVG))) != 0 {
 		vipsTypeSupportLoad[imageTypeSVG] = true
 	}
+	if int(C.vips_type_find_load_go(C.int(imageTypeICO))) != 0 {
+		vipsTypeSupportLoad[imageTypeICO] = true
+	}
 	if int(C.vips_type_find_load_go(C.int(imageTypeHEIC))) != 0 {
 		vipsTypeSupportLoad[imageTypeHEIC] = true
 	}
@@ -95,9 +101,6 @@ func initVips() {
 		vipsTypeSupportLoad[imageTypeTIFF] = true
 	}
 
-	// we load ICO with github.com/mat/besticon/ico and send decoded data to vips
-	vipsTypeSupportLoad[imageTypeICO] = true
-
 	if int(C.vips_type_find_save_go(C.int(imageTypeJPEG))) != 0 {
 		vipsTypeSupportSave[imageTypeJPEG] = true
 	}
@@ -197,12 +200,12 @@ func (img *vipsImage) Load(data []byte, imgtype imageType, shrink int, scale flo
 	case imageTypeSVG:
 		err = C.vips_svgload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), C.double(scale), &tmp)
 	case imageTypeICO:
-		rawData, width, height, icoErr := icoData(data)
-		if icoErr != nil {
-			return icoErr
+		bestPage, ierr := imageSize.BestIcoPage(bytes.NewBuffer(data))
+		if ierr != nil {
+			logWarning(ierr.Error())
 		}
 
-		tmp = C.vips_image_new_from_memory_copy(unsafe.Pointer(&rawData[0]), C.size_t(width*height*4), C.int(width), C.int(height), 4, C.VIPS_FORMAT_UCHAR)
+		err = C.vips_icoload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), C.int(bestPage), &tmp)
 	case imageTypeHEIC:
 		err = C.vips_heifload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), &tmp)
 	case imageTypeTIFF:

+ 1 - 0
vips.h

@@ -31,6 +31,7 @@ int vips_pngload_go(void *buf, size_t len, VipsImage **out);
 int vips_webpload_go(void *buf, size_t len, double scale, int pages, VipsImage **out);
 int vips_gifload_go(void *buf, size_t len, int pages, VipsImage **out);
 int vips_svgload_go(void *buf, size_t len, double scale, VipsImage **out);
+int vips_icoload_go(void *buf, size_t len, int page, VipsImage **out);
 int vips_heifload_go(void *buf, size_t len, VipsImage **out);
 int vips_tiffload_go(void *buf, size_t len, VipsImage **out);