Skip to content

Commit

Permalink
Go 1.5.3 -> Go 1.6.2. VIPS 8.2.3 -> 8.3.0. Replace ImageMagick with G…
Browse files Browse the repository at this point in the history
…iflib for loading GIFs. Pre-shrink WebP.
  • Loading branch information
aaron42net committed Apr 28, 2016
1 parent 203b9fa commit 80d42ac
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 79 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Expand Up @@ -45,7 +45,7 @@ RUN \


# And remove almost everything else that we installed # And remove almost everything else that we installed
apt-get remove -y git automake build-essential libglib2.0-dev libjpeg-dev libpng12-dev \ apt-get remove -y git automake build-essential libglib2.0-dev libjpeg-dev libpng12-dev \
libwebp-dev libtiff5-dev libexif-dev libmagickwand-dev libfftw3-dev libffi-dev && \ libwebp-dev libtiff5-dev libexif-dev libgif-dev libfftw3-dev libffi-dev && \
apt-get autoremove -y && \ apt-get autoremove -y && \
apt-get autoclean && \ apt-get autoclean && \
apt-get clean && \ apt-get clean && \
Expand Down
2 changes: 1 addition & 1 deletion cmd/fotomat/version.go
Expand Up @@ -8,7 +8,7 @@ import (


const ( const (
// FotomatVersion is updated by git-hooks/pre-commit // FotomatVersion is updated by git-hooks/pre-commit
FotomatVersion = "2.4.175" FotomatVersion = "2.4.176"
) )


var ( var (
Expand Down
11 changes: 5 additions & 6 deletions format/format.go
Expand Up @@ -25,13 +25,12 @@ var formatInfo = []struct {
mime string mime string
loadFile func(filename string) (*vips.Image, error) loadFile func(filename string) (*vips.Image, error)
loadBytes func([]byte) (*vips.Image, error) loadBytes func([]byte) (*vips.Image, error)
metadata func([]byte) (Metadata, error)
}{ }{
{mime: "application/octet-stream", loadFile: nil, loadBytes: nil, metadata: nil}, {mime: "application/octet-stream", loadFile: nil, loadBytes: nil},
{mime: "image/jpeg", loadFile: vips.Jpegload, loadBytes: vips.JpegloadBuffer, metadata: nil}, {mime: "image/jpeg", loadFile: vips.Jpegload, loadBytes: vips.JpegloadBuffer},
{mime: "image/png", loadFile: vips.Pngload, loadBytes: vips.PngloadBuffer, metadata: nil}, {mime: "image/png", loadFile: vips.Pngload, loadBytes: vips.PngloadBuffer},
{mime: "image/gif", loadFile: vips.Magickload, loadBytes: vips.MagickloadBuffer, metadata: metadataGif}, {mime: "image/gif", loadFile: vips.Gifload, loadBytes: vips.GifloadBuffer},
{mime: "image/webp", loadFile: vips.Webpload, loadBytes: vips.WebploadBuffer, metadata: nil}, {mime: "image/webp", loadFile: vips.Webpload, loadBytes: vips.WebploadBuffer},
} }


func DetectFormat(blob []byte) Format { func DetectFormat(blob []byte) Format {
Expand Down
17 changes: 0 additions & 17 deletions format/metadata.go
@@ -1,9 +1,7 @@
package format package format


import ( import (
"bytes"
"github.com/die-net/fotomat/vips" "github.com/die-net/fotomat/vips"
"image/gif"
) )


type Metadata struct { type Metadata struct {
Expand All @@ -24,10 +22,6 @@ func MetadataBytes(blob []byte) (Metadata, error) {
} }


func (format Format) MetadataBytes(blob []byte) (Metadata, error) { func (format Format) MetadataBytes(blob []byte) (Metadata, error) {
if metadata := formatInfo[format].metadata; metadata != nil {
return metadata(blob)
}

image, err := format.LoadBytes(blob) image, err := format.LoadBytes(blob)
if err != nil { if err != nil {
return Metadata{}, ErrUnknownFormat return Metadata{}, ErrUnknownFormat
Expand Down Expand Up @@ -63,14 +57,3 @@ func MetadataImage(image *vips.Image) Metadata {
} }
return Metadata{Width: w, Height: h, Orientation: o, HasAlpha: image.HasAlpha()} return Metadata{Width: w, Height: h, Orientation: o, HasAlpha: image.HasAlpha()}
} }

// vips.MagickloadBuffer completely decodes the image, which is slow and
// unsafe, as we can't check the size before decode. Use Go's GIF reader
// to fetch metadata instead.
func metadataGif(blob []byte) (Metadata, error) {
c, err := gif.DecodeConfig(bytes.NewReader(blob))
if err != nil {
return Metadata{}, err
}
return Metadata{Width: c.Width, Height: c.Height, Orientation: Undefined, Format: Gif}, nil
}
20 changes: 10 additions & 10 deletions preinstall.sh
Expand Up @@ -7,8 +7,8 @@ set -euo pipefail


# Usage: sudo ./preinstall.sh # Usage: sudo ./preinstall.sh


VIPS_VERSION=${VIPS_VERSION:-8.2.3} VIPS_VERSION=${VIPS_VERSION:-8.3.0}
GO_VERSION=${GO_VERSION:-1.5.3} GO_VERSION=${GO_VERSION:-1.6.2}


export PATH="/usr/local/bin:/usr/bin:/bin:${PATH:-}" export PATH="/usr/local/bin:/usr/bin:/bin:${PATH:-}"
export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:/usr/lib/pkgconfig:${PKG_CONFIG_PATH:-}" export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:/usr/lib/pkgconfig:${PKG_CONFIG_PATH:-}"
Expand Down Expand Up @@ -42,23 +42,23 @@ case "$release" in
debian-[89]|debian-unknown|ubuntu-1[456].*|mint-17.*) debian-[89]|debian-unknown|ubuntu-1[456].*|mint-17.*)
# Debian 8-9 or sid, Ubuntu 14-16, Mint 17 # Debian 8-9 or sid, Ubuntu 14-16, Mint 17
apt-get -q update apt-get -q update
apt-get install -y -q --no-install-recommends ca-certificates git curl tar automake build-essential libglib2.0-dev libjpeg-dev libpng12-dev libwebp-dev libtiff5-dev libexif-dev libmagickwand-dev libfftw3-dev libffi-dev apt-get install -y -q --no-install-recommends ca-certificates git curl tar automake build-essential libglib2.0-dev libjpeg-dev libpng12-dev libwebp-dev libgif-dev libtiff5-dev libexif-dev libfftw3-dev libffi-dev
;; ;;
centos-7*|rhel-7*) centos-7*|rhel-7*)
# RHEL/CentOS/SL 7 # RHEL/CentOS/SL 7
yum -y install epel-release yum -y install epel-release
yum -y update yum -y update
yum install -y curl tar findutils git automake make gcc gcc-c++ glib2-devel ImageMagick-devel libexif-devel libjpeg-turbo-devel libpng-devel libtiff-devel libwebp-devel libxml2-devel libffi-devel jbigkit-devel yum install -y curl tar findutils git automake make gcc gcc-c++ glib2-devel libexif-devel libjpeg-turbo-devel libpng-devel libtiff-devel libwebp-devel giflib-devel libxml2-devel libffi-devel jbigkit-devel
;; ;;
fedora-2[1-3]) fedora-2[1-3])
# Fedora 21-23 # Fedora 21-23
yum install -y curl tar findutils git automake make gcc gcc-c++ glib2-devel ImageMagick-devel libexif-devel libjpeg-turbo-devel libpng-devel libtiff-devel libwebp-devel libxml2-devel libffi-devel jbigkit-devel fftw3-devel fontconfig-devel libtool-ltdl-devel yum install -y curl tar findutils git automake make gcc gcc-c++ glib2-devel libexif-devel libjpeg-turbo-devel libpng-devel libtiff-devel libwebp-devel giflib-devel libxml2-devel libffi-devel jbigkit-devel fftw3-devel fontconfig-devel libtool-ltdl-devel
;; ;;
"Red Hat Enterprise Linux release 6."*|"CentOS release 6."*|"Scientific Linux release 6."*) "Red Hat Enterprise Linux release 6."*|"CentOS release 6."*|"Scientific Linux release 6."*)
# RHEL/CentOS/SL 6 # RHEL/CentOS/SL 6
yum -y install epel-release yum -y install epel-release
yum -y update yum -y update
yum install -y curl tar findutils git automake make gcc gcc-c++ glib2-devel ImageMagick-devel libexif-devel libjpeg-turbo-devel libpng-devel libtiff-devel libwebp-devel libxml2-devel yum install -y curl tar findutils git automake make gcc gcc-c++ glib2-devel libexif-devel libjpeg-turbo-devel libpng-devel libtiff-devel libwebp-devel giflib-devel libxml2-devel
;; ;;
*) *)
echo "Sorry, I don't yet know how to install on $release ($(uname -a))." echo "Sorry, I don't yet know how to install on $release ($(uname -a))."
Expand All @@ -81,10 +81,10 @@ else
curl -sS http://www.vips.ecs.soton.ac.uk/supported/${VIPS_VERSION%.*}/vips-${VIPS_VERSION}.tar.gz | \ curl -sS http://www.vips.ecs.soton.ac.uk/supported/${VIPS_VERSION%.*}/vips-${VIPS_VERSION}.tar.gz | \
tar --strip-components=1 -C vips-$VIPS_VERSION -xzf - tar --strip-components=1 -C vips-$VIPS_VERSION -xzf -
cd vips-$VIPS_VERSION cd vips-$VIPS_VERSION
./configure --disable-debug --disable-dependency-tracking --disable-static --without-orc \ ./configure --disable-debug --disable-dependency-tracking --disable-static --without-orc --without-magick \
--with-OpenEXR --with-jpeg --with-lcms --with-libexif --with-magick \ --with-OpenEXR --with-jpeg --with-lcms --with-libexif --with-giflib \
--with-tiff --with-libwebp --with-png ${VIPS_OPTIONS:-} --with-tiff --with-libwebp --with-png ${VIPS_OPTIONS:-}
make make -j $( getconf _NPROCESSORS_ONLN 2> /dev/null || echo 1 )
make install make install
cd .. cd ..
rm -rf vips-$VIPS_VERSION rm -rf vips-$VIPS_VERSION
Expand All @@ -106,7 +106,7 @@ else
mkdir -p /usr/local/go && \ mkdir -p /usr/local/go && \
curl -sS https://storage.googleapis.com/golang/go${GO_VERSION}.${arch}.tar.gz | \ curl -sS https://storage.googleapis.com/golang/go${GO_VERSION}.${arch}.tar.gz | \
tar --strip-components=1 -C /usr/local/go -xzf - tar --strip-components=1 -C /usr/local/go -xzf -
ln -s ../go/bin/go /usr/local/bin ln -sf ../go/bin/go /usr/local/bin
echo "Installed $(go version)" echo "Installed $(go version)"
fi fi


Expand Down
15 changes: 10 additions & 5 deletions thumbnail/thumbnail.go
Expand Up @@ -42,9 +42,9 @@ func Thumbnail(blob []byte, o Options) ([]byte, error) {
// Are we shrinking by more than 2.5%? // Are we shrinking by more than 2.5%?
shrinking := iw < m.Width-m.Width/40 && ih < m.Height-m.Height/40 shrinking := iw < m.Width-m.Width/40 && ih < m.Height-m.Height/40


// Figure out the jpeg shrink factor and load image. // Figure out the jpeg/webp shrink factor and load image.
// Jpeg shrink rounds up the number of pixels. // Jpeg shrink rounds up the number of pixels.
psf := preShrinkFactor(m.Width, m.Height, iw, ih, trustWidth, o.FastResize) psf := preShrinkFactor(m.Width, m.Height, iw, ih, trustWidth, o.FastResize, m.Format == format.Jpeg)
image, err := load(blob, m.Format, psf) image, err := load(blob, m.Format, psf)
if err != nil { if err != nil {
return nil, err return nil, err
Expand Down Expand Up @@ -89,8 +89,12 @@ func Thumbnail(blob []byte, o Options) ([]byte, error) {
} }


func load(blob []byte, f format.Format, shrink int) (*vips.Image, error) { func load(blob []byte, f format.Format, shrink int) (*vips.Image, error) {
if f == format.Jpeg && shrink > 1 { if shrink > 1 {
return vips.JpegloadBufferShrink(blob, shrink) if f == format.Jpeg {
return vips.JpegloadBufferShrink(blob, shrink)
} else if f == format.Webp {
return vips.WebploadBufferShrink(blob, shrink)
}
} }


return f.LoadBytes(blob) return f.LoadBytes(blob)
Expand Down Expand Up @@ -146,7 +150,8 @@ func resize(image *vips.Image, iw, ih int, fastResize bool, blurSigma float64, s


// If necessary, do a high-quality resize to scale to final size. // If necessary, do a high-quality resize to scale to final size.
if iw < m.Width || ih < m.Height { if iw < m.Width || ih < m.Height {
if err := image.Resize(float64(iw)/float64(m.Width), float64(ih)/float64(m.Height)); err != nil { // Vips 8.3 sometimes produces 1px smaller images than desired without the rounding help here.
if err := image.Resize((float64(iw)+0.5)/float64(m.Width), (float64(ih)+0.5)/float64(m.Height)); err != nil {
return err return err
} }
} }
Expand Down
2 changes: 1 addition & 1 deletion thumbnail/thumbnail_test.go
Expand Up @@ -114,7 +114,7 @@ func TestBlurSharpen(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
l := len(thumb) l := len(thumb)


thumb, err = Thumbnail(img, Options{Width: 300, Height: 400, BlurSigma: 0.5}) thumb, err = Thumbnail(img, Options{Width: 300, Height: 400, BlurSigma: 0.8})
if assert.Nil(t, err) { if assert.Nil(t, err) {
assert.True(t, len(thumb) < l) // Blurry photos will be smaller assert.True(t, len(thumb) < l) // Blurry photos will be smaller
} }
Expand Down
27 changes: 19 additions & 8 deletions thumbnail/utils.go
Expand Up @@ -31,11 +31,11 @@ func scaleAspect(ow, oh, rw, rh int, within bool) (int, int, bool) {
return rw, rh, trustWidth return rw, rh, trustWidth
} }


func preShrinkFactor(mw, mh, iw, ih int, trustWidth, fastResize bool) int { func preShrinkFactor(mw, mh, iw, ih int, trustWidth, fastResize, jpeg bool) int {
// Jpeg shrink rounds up the number of pixels, so calculate // Jpeg shrink rounds up the number of pixels, so calculate
// pre-shrink based on side that matters more. // pre-shrink based on side that matters more. Webp rounds down.
var shrink float64 var shrink float64
if trustWidth { if trustWidth == jpeg {
shrink = float64(mw) / float64(iw) shrink = float64(mw) / float64(iw)
} else { } else {
shrink = float64(mh) / float64(ih) shrink = float64(mh) / float64(ih)
Expand All @@ -48,13 +48,24 @@ func preShrinkFactor(mw, mh, iw, ih int, trustWidth, fastResize bool) int {
} }


// Jpeg loader can quickly shrink by 2, 4, or 8. // Jpeg loader can quickly shrink by 2, 4, or 8.
if jpeg {
switch {
case shrink >= 8:
return 8
case shrink >= 4:
return 4
case shrink >= 2:
return 2
default:
return 1
}
}

switch { switch {
case shrink >= 8: case shrink >= 1024:
return 8 return 1024
case shrink >= 4:
return 4
case shrink >= 2: case shrink >= 2:
return 2 return int(shrink)
default: default:
return 1 return 1
} }
Expand Down
46 changes: 30 additions & 16 deletions vips/foreign.go
Expand Up @@ -10,6 +10,20 @@ import (
"unsafe" "unsafe"
) )


func Gifload(filename string) (*Image, error) {
var out *C.struct__VipsImage
cf := C.CString(filename)
e := C.cgo_vips_gifload(cf, &out)
C.free(unsafe.Pointer(cf))
return loadError(out, e)
}

func GifloadBuffer(buf []byte) (*Image, error) {
var out *C.struct__VipsImage
e := C.cgo_vips_gifload_buffer(unsafe.Pointer(&buf[0]), C.size_t(len(buf)), &out)
return loadError(out, e)
}

func Jpegload(filename string) (*Image, error) { func Jpegload(filename string) (*Image, error) {
var out *C.struct__VipsImage var out *C.struct__VipsImage
cf := C.CString(filename) cf := C.CString(filename)
Expand Down Expand Up @@ -47,20 +61,6 @@ func (in *Image) JpegsaveBuffer(strip bool, q int, optimizeCoding, interlace boo
return saveError(ptr, length, e) return saveError(ptr, length, e)
} }


func Magickload(filename string) (*Image, error) {
var out *C.struct__VipsImage
cf := C.CString(filename)
e := C.cgo_vips_magickload(cf, &out)
C.free(unsafe.Pointer(cf))
return loadError(out, e)
}

func MagickloadBuffer(buf []byte) (*Image, error) {
var out *C.struct__VipsImage
e := C.cgo_vips_magickload_buffer(unsafe.Pointer(&buf[0]), C.size_t(len(buf)), &out)
return loadError(out, e)
}

func Pngload(filename string) (*Image, error) { func Pngload(filename string) (*Image, error) {
var out *C.struct__VipsImage var out *C.struct__VipsImage
cf := C.CString(filename) cf := C.CString(filename)
Expand All @@ -87,14 +87,28 @@ func (in *Image) PngsaveBuffer(compression int, interlace bool) ([]byte, error)
func Webpload(filename string) (*Image, error) { func Webpload(filename string) (*Image, error) {
var out *C.struct__VipsImage var out *C.struct__VipsImage
cf := C.CString(filename) cf := C.CString(filename)
e := C.cgo_vips_webpload(cf, &out) e := C.cgo_vips_webpload(cf, &out, 1)
C.free(unsafe.Pointer(cf))
return loadError(out, e)
}

func WebploadShrink(filename string, shrink int) (*Image, error) {
var out *C.struct__VipsImage
cf := C.CString(filename)
e := C.cgo_vips_webpload(cf, &out, C.int(shrink))
C.free(unsafe.Pointer(cf)) C.free(unsafe.Pointer(cf))
return loadError(out, e) return loadError(out, e)
} }


func WebploadBuffer(buf []byte) (*Image, error) { func WebploadBuffer(buf []byte) (*Image, error) {
var out *C.struct__VipsImage var out *C.struct__VipsImage
e := C.cgo_vips_webpload_buffer(unsafe.Pointer(&buf[0]), C.size_t(len(buf)), &out) e := C.cgo_vips_webpload_buffer(unsafe.Pointer(&buf[0]), C.size_t(len(buf)), &out, 1)
return loadError(out, e)
}

func WebploadBufferShrink(buf []byte, shrink int) (*Image, error) {
var out *C.struct__VipsImage
e := C.cgo_vips_webpload_buffer(unsafe.Pointer(&buf[0]), C.size_t(len(buf)), &out, C.int(shrink))
return loadError(out, e) return loadError(out, e)
} }


Expand Down
28 changes: 14 additions & 14 deletions vips/foreign.h
Expand Up @@ -3,28 +3,28 @@
#include <vips/vips7compat.h> #include <vips/vips7compat.h>


int int
cgo_vips_jpegload(const char *filename, VipsImage **out, int shrink) { cgo_vips_gifload(const char *filename, VipsImage **out) {
return vips_jpegload(filename, out, "access", VIPS_ACCESS_SEQUENTIAL, "shrink", shrink, NULL); return vips_gifload(filename, out, NULL);
} }


int int
cgo_vips_jpegload_buffer(void *buf, size_t len, VipsImage **out, int shrink) { cgo_vips_gifload_buffer(void *buf, size_t len, VipsImage **out) {
return vips_jpegload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, "shrink", shrink, NULL); return vips_gifload_buffer(buf, len, out, NULL);
} }


int int
cgo_vips_jpegsave_buffer(VipsImage *in, void **buf, size_t *len, int strip, int q, int optimize_coding, int interlace) { cgo_vips_jpegload(const char *filename, VipsImage **out, int shrink) {
return vips_jpegsave_buffer(in, buf, len, "strip", strip, "Q", q, "optimize_coding", optimize_coding, "interlace", interlace, NULL); return vips_jpegload(filename, out, "access", VIPS_ACCESS_SEQUENTIAL, "shrink", shrink, NULL);
} }


int int
cgo_vips_magickload(const char *filename, VipsImage **out) { cgo_vips_jpegload_buffer(void *buf, size_t len, VipsImage **out, int shrink) {
return vips_magickload(filename, out, NULL); return vips_jpegload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, "shrink", shrink, NULL);
} }


int int
cgo_vips_magickload_buffer(void *buf, size_t len, VipsImage **out) { cgo_vips_jpegsave_buffer(VipsImage *in, void **buf, size_t *len, int strip, int q, int optimize_coding, int interlace) {
return vips_magickload_buffer(buf, len, out, NULL); return vips_jpegsave_buffer(in, buf, len, "strip", strip, "Q", q, "optimize_coding", optimize_coding, "interlace", interlace, NULL);
} }


int int
Expand All @@ -43,13 +43,13 @@ cgo_vips_pngsave_buffer(VipsImage *in, void **buf, size_t *len, int compression,
} }


int int
cgo_vips_webpload(const char *filename, VipsImage **out) { cgo_vips_webpload(const char *filename, VipsImage **out, int shrink) {
return vips_webpload(filename, out, NULL); return vips_webpload(filename, out, "shrink", shrink, NULL);
} }


int int
cgo_vips_webpload_buffer(void *buf, size_t len, VipsImage **out) { cgo_vips_webpload_buffer(void *buf, size_t len, VipsImage **out, int shrink) {
return vips_webpload_buffer(buf, len, out, NULL); return vips_webpload_buffer(buf, len, out, "shrink", shrink, NULL);
} }


int int
Expand Down

0 comments on commit 80d42ac

Please sign in to comment.