diff --git a/README.md b/README.md index b3c65db..eacdc75 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ _libvips_ can take advantage of [liborc](http://code.entropywave.com/orc/) if pr ### Install libvips on Mac OS - brew install vips --without-fftw --without-libexif --without-libgsf \ + brew install vips --with-webp --without-fftw --without-libexif --without-libgsf \ --without-little-cms2 --without-orc --without-pango --without-pygobject3 \ --without-gobject-introspection --without-python @@ -36,12 +36,13 @@ _libvips_ can take advantage of [liborc](http://code.entropywave.com/orc/) if pr Compiling from source is recommended: sudo apt-get install automake build-essential git gobject-introspection \ - libglib2.0-dev libjpeg-turbo8-dev libpng12-dev gtk-doc-tools + libglib2.0-dev webp libjpeg-turbo8-dev libpng12-dev gtk-doc-tools git clone https://github.com/jcupitt/libvips.git cd libvips ./bootstrap.sh - ./configure --enable-debug=no --without-python --without-fftw --without-libexif \ - --without-libgf --without-little-cms --without-orc --without-pango --prefix=/usr + ./configure --enable-debug=no --with-webp --without-python --without-fftw \ + --without-libexif --without-libgf --without-little-cms --without-orc \ + --without-pango --prefix=/usr make sudo make install sudo ldconfig diff --git a/testdata/1.jpg b/fixtures/1.jpg similarity index 100% rename from testdata/1.jpg rename to fixtures/1.jpg diff --git a/testdata/2.jpg b/fixtures/2.jpg similarity index 100% rename from testdata/2.jpg rename to fixtures/2.jpg diff --git a/testdata/3.jpg b/fixtures/3.jpg similarity index 100% rename from testdata/3.jpg rename to fixtures/3.jpg diff --git a/testdata/4.jpg b/fixtures/4.jpg similarity index 100% rename from testdata/4.jpg rename to fixtures/4.jpg diff --git a/testdata/5.jpg b/fixtures/5.jpg similarity index 100% rename from testdata/5.jpg rename to fixtures/5.jpg diff --git a/testdata/6.png b/fixtures/6.png similarity index 100% rename from testdata/6.png rename to fixtures/6.png diff --git a/testdata/7.png b/fixtures/7.png similarity index 100% rename from testdata/7.png rename to fixtures/7.png diff --git a/fixtures/test.webp b/fixtures/test.webp new file mode 100644 index 0000000..48c4a68 Binary files /dev/null and b/fixtures/test.webp differ diff --git a/vips.go b/vips.go index 6605347..8512008 100644 --- a/vips.go +++ b/vips.go @@ -7,10 +7,10 @@ package vips import "C" import ( - "bytes" "errors" "fmt" "math" + "net/http" "os" "runtime" "unsafe" @@ -18,9 +18,10 @@ import ( const DEBUG = false -var ( - MARKER_JPEG = []byte{0xff, 0xd8} - MARKER_PNG = []byte{0x89, 0x50} +const ( + JPEG_MIME = "image/jpeg" + WEBP_MIME = "image/webp" + PNG_MIME = "image/png" ) type ImageType int @@ -28,9 +29,12 @@ type ImageType int const ( UNKNOWN ImageType = iota JPEG + WEBP PNG ) +const QUALITY = 80 + type Interpolator int const ( @@ -69,13 +73,15 @@ type Options struct { func init() { runtime.LockOSThread() defer runtime.UnlockOSThread() + err := C.vips_initialize() if err != 0 { C.vips_shutdown() panic("unable to start vips!") } + C.vips_concurrency_set(1) - C.vips_cache_set_max_mem(100 * 1048576) // 100Mb + C.vips_cache_set_max_mem(100 * 1024 * 1024) C.vips_cache_set_max(500) } @@ -89,9 +95,11 @@ func Resize(buf []byte, o Options) ([]byte, error) { // detect (if possible) the file type typ := UNKNOWN switch { - case bytes.Equal(buf[:2], MARKER_JPEG): + case http.DetectContentType(buf) == JPEG_MIME: typ = JPEG - case bytes.Equal(buf[:2], MARKER_PNG): + case http.DetectContentType(buf) == WEBP_MIME: + typ = WEBP + case http.DetectContentType(buf) == PNG_MIME: typ = PNG default: return nil, errors.New("unknown image format") @@ -101,17 +109,22 @@ func Resize(buf []byte, o Options) ([]byte, error) { var image, tmpImage *C.struct__VipsImage // feed it + imageLength := C.size_t(len(buf)) + imageBuf := unsafe.Pointer(&buf[0]) + switch typ { case JPEG: - C.vips_jpegload_buffer_seq(unsafe.Pointer(&buf[0]), C.size_t(len(buf)), &image) + C.vips_jpegload_buffer_seq(imageBuf, imageLength, &image) + case WEBP: + C.vips_webpload_buffer_seq(imageBuf, imageLength, &image) case PNG: - C.vips_pngload_buffer_seq(unsafe.Pointer(&buf[0]), C.size_t(len(buf)), &image) + C.vips_pngload_buffer_seq(imageBuf, imageLength, &image) } defer C.vips_thread_shutdown() // defaults if o.Quality == 0 { - o.Quality = 100 + o.Quality = QUALITY } // get WxH diff --git a/vips.h b/vips.h index e02a4e4..4e1f16a 100644 --- a/vips.h +++ b/vips.h @@ -26,6 +26,12 @@ vips_jpegload_buffer_shrink(void *buf, size_t len, VipsImage **out, int shrink) return vips_jpegload_buffer(buf, len, out, "shrink", shrink, NULL); }; +int +vips_webpload_buffer_seq(void *buf, size_t len, VipsImage **out) +{ + return vips_webpload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL); +}; + int vips_pngload_buffer_seq(void *buf, size_t len, VipsImage **out) { diff --git a/vips_test.go b/vips_test.go index 608719c..34338fd 100644 --- a/vips_test.go +++ b/vips_test.go @@ -5,13 +5,14 @@ import ( "image" "image/jpeg" "io/ioutil" + "net/http" "os" "testing" ) func BenchmarkParallel(b *testing.B) { options := Options{Width: 800, Height: 600, Crop: true} - f, err := os.Open("testdata/1.jpg") + f, err := os.Open("fixtures/1.jpg") if err != nil { b.Fatal(err) } @@ -34,7 +35,7 @@ func BenchmarkParallel(b *testing.B) { func BenchmarkSerialized(b *testing.B) { options := Options{Width: 800, Height: 600, Crop: true} - f, err := os.Open("testdata/1.jpg") + f, err := os.Open("fixtures/1.jpg") if err != nil { b.Fatal(err) } @@ -108,15 +109,40 @@ func TestResize(t *testing.T) { newWidth := uint(outImg.Bounds().Dx()) newHeight := uint(outImg.Bounds().Dy()) + if newWidth != mt.expectedWidth || newHeight != mt.expectedHeight { t.Fatalf("%d. Resize(imgData, %#v) => "+ "width: %v, height: %v, want width: %v, height: %v, "+ "originl size: %vx%v", index, options, - newWidth, newHeight, mt.expectedWidth, mt.expectedHeight, + newWidth, newHeight, + mt.expectedWidth, mt.expectedHeight, mt.origWidth, mt.origHeight, ) } } } + +func TestWebpResize(t *testing.T) { + options := Options{Width: 800, Height: 600, Crop: true} + img, err := os.Open("fixtures/test.webp") + if err != nil { + t.Fatal(err) + } + defer img.Close() + + buf, err := ioutil.ReadAll(img) + if err != nil { + t.Fatal(err) + } + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + if http.DetectContentType(newImg) != JPEG_MIME { + t.Fatal("Image is not webp valid") + } +}