From d471c49348f38c852c2352693a805f39bda47131 Mon Sep 17 00:00:00 2001 From: Tomas Aparicio Date: Sun, 5 Apr 2015 22:01:48 +0200 Subject: [PATCH] feat(#6, #10, #11) --- image.go | 50 +++++++++++++++++++++++----- options.go | 22 ++++++------- resize.go | 96 ++++++++++++++++++++++++++++++++++++++++++++---------- vips.go | 23 +++++++------ vips.h | 24 ++++++++++++++ 5 files changed, 168 insertions(+), 47 deletions(-) diff --git a/image.go b/image.go index 2b658b3..7ee0f8e 100644 --- a/image.go +++ b/image.go @@ -1,12 +1,46 @@ package bimg -/* -#cgo pkg-config: vips -#include "vips/vips.h" -*/ -import "C" - type Image struct { - buf []byte - image *C.struct__VipsImage + buffer []byte +} + +func (i *Image) Resize(width int, height int) ([]byte, error) { + options := Options{ + Width: width, + Height: height, + } + return Resize(i.buffer, options) +} + +func (i *Image) Extract(top int, left int, width int, height int) ([]byte, error) { + options := Options{ + Width: width, + Height: height, + Top: top, + Left: left, + } + return Resize(i.buffer, options) +} + +func (i *Image) Rotate(degrees Angle) ([]byte, error) { + options := Options{Rotate: degrees} + return Resize(i.buffer, options) +} + +func (i *Image) Flip() ([]byte, error) { + options := Options{Flip: VERTICAL} + return Resize(i.buffer, options) +} + +func (i *Image) Flop() ([]byte, error) { + options := Options{Flip: HORIZONTAL} + return Resize(i.buffer, options) +} + +func (i *Image) Type() string { + return DetermineImageTypeName(i.buffer) +} + +func NewImage(buf []byte) *Image { + return &Image{buf} } diff --git a/options.go b/options.go index 8b5ea6b..60db5b8 100644 --- a/options.go +++ b/options.go @@ -22,18 +22,14 @@ func (i Interpolator) String() string { return interpolations[i] } -type Rotation struct { - angle int -} +type Angle int -func (a Rotation) calculate() int { - angle := a.angle - divisor := angle % 90 - if divisor != 0 { - angle = a.angle - divisor - } - return angle -} +const ( + D0 Angle = C.VIPS_ANGLE_D0 + D90 Angle = C.VIPS_ANGLE_D90 + D180 Angle = C.VIPS_ANGLE_D180 + D270 Angle = C.VIPS_ANGLE_D270 +) type Direction int @@ -45,12 +41,14 @@ const ( type Options struct { Height int Width int + Top int + Left int Crop bool Enlarge bool Extend int Embed bool Quality int - Rotate int + Rotate Angle Flip Direction Gravity Gravity Interpolator Interpolator diff --git a/resize.go b/resize.go index d5e78bf..8938f34 100644 --- a/resize.go +++ b/resize.go @@ -16,8 +16,15 @@ const ( NOHALO ) +const ( + CENTRE Gravity = iota + NORTH + EAST + SOUTH + WEST +) + func Resize(buf []byte, o Options) ([]byte, error) { - // detect (if possible) the file type defer C.vips_thread_shutdown() image, err := vipsRead(buf) @@ -34,7 +41,6 @@ func Resize(buf []byte, o Options) ([]byte, error) { inWidth := int(image.Xsize) inHeight := int(image.Ysize) - // crop if o.Crop { left, top := calculateCrop(inWidth, inHeight, o.Width, o.Height, o.Gravity) o.Width = int(math.Min(float64(inWidth), float64(o.Width))) @@ -45,9 +51,24 @@ func Resize(buf []byte, o Options) ([]byte, error) { } } - // rotate + rotation, flip := calculateRotationAndFlip(image, o.Rotate) + if flip { + o.Flip = HORIZONTAL + } + if rotation != D0 { + o.Rotate = rotation + } + if o.Rotate > 0 { - image, err = Rotate(image, Rotation{o.Rotate}) + rotation := calculateRotation(o.Rotate) + image, err = vipsRotate(image, rotation) + if err != nil { + return nil, err + } + } + + if o.Flip > 0 { + image, err = vipsFlip(image, o.Flip) if err != nil { return nil, err } @@ -62,21 +83,9 @@ func Resize(buf []byte, o Options) ([]byte, error) { return buf, nil } -func Rotate(image *C.struct__VipsImage, r Rotation) (*C.struct__VipsImage, error) { - //vips := &Vips{} - return vipsRotate(image, r.calculate()) -} - -const ( - CENTRE Gravity = iota - NORTH - EAST - SOUTH - WEST -) - func calculateCrop(inWidth, inHeight, outWidth, outHeight int, gravity Gravity) (int, int) { left, top := 0, 0 + switch gravity { case NORTH: left = (inWidth - outWidth + 1) / 2 @@ -92,5 +101,58 @@ func calculateCrop(inWidth, inHeight, outWidth, outHeight int, gravity Gravity) left = (inWidth - outWidth + 1) / 2 top = (inHeight - outHeight + 1) / 2 } + return left, top } + +func calculateRotationAndFlip(image *C.struct__VipsImage, angle Angle) (Angle, bool) { + rotate := D0 + flip := false + + if angle == -1 { + switch vipsExifOrientation(image) { + case 6: + rotate = D90 + break + case 3: + rotate = D180 + break + case 8: + rotate = D270 + break + case 2: + flip = true + break // flip 1 + case 7: + flip = true + rotate = D90 + break // flip 6 + case 4: + flip = true + rotate = D180 + break // flip 3 + case 5: + flip = true + rotate = D270 + break // flip 8 + } + } else { + if angle == 90 { + rotate = D90 + } else if angle == 180 { + rotate = D180 + } else if angle == 270 { + rotate = D270 + } + } + + return rotate, flip +} + +func calculateRotation(angle Angle) Angle { + divisor := angle % 90 + if divisor != 0 { + angle = angle - divisor + } + return angle +} diff --git a/vips.go b/vips.go index 62fb727..57c1e05 100644 --- a/vips.go +++ b/vips.go @@ -13,7 +13,7 @@ import ( "unsafe" ) -type vipsImage *C.struct__VipsImage +type vipsImage C.struct__VipsImage func init() { runtime.LockOSThread() @@ -34,13 +34,13 @@ type Vips struct { buf []byte } -func vipsRotate(image *C.struct__VipsImage, degrees int) (*C.struct__VipsImage, error) { +func vipsRotate(image *C.struct__VipsImage, angle Angle) (*C.struct__VipsImage, error) { var out *C.struct__VipsImage - err := C.vips_rotate(image, &out, C.int(degrees)) + err := C.vips_rotate(image, &out, C.int(angle)) C.g_object_unref(C.gpointer(image)) if err != 0 { - return nil, vipsError() + return nil, catchVipsError() } defer C.g_object_unref(C.gpointer(out)) @@ -53,7 +53,7 @@ func vipsFlip(image *C.struct__VipsImage, direction Direction) (*C.struct__VipsI err := C.vips_flip_seq(image, &out) C.g_object_unref(C.gpointer(image)) if err != 0 { - return nil, vipsError() + return nil, catchVipsError() } defer C.g_object_unref(C.gpointer(out)) @@ -74,9 +74,8 @@ func vipsRead(buf []byte) (*C.struct__VipsImage, error) { imageTypeC := C.int(imageType) err := C.vips_init_image(imageBuf, length, imageTypeC, &image) - //err := C.vips_jpegload_buffer_seq(imageBuf, length, &image) if err != 0 { - return nil, vipsError() + return nil, catchVipsError() } return image, nil @@ -88,7 +87,7 @@ func vipsExtract(image *C.struct__VipsImage, left int, top int, width int, heigh err := C.vips_extract_area_0(image, &buf, C.int(left), C.int(top), C.int(width), C.int(height)) C.g_object_unref(C.gpointer(image)) if err != 0 { - return nil, vipsError() + return nil, catchVipsError() } return buf, nil @@ -122,6 +121,10 @@ func vipsImageType(buf []byte) int { return imageType } +func vipsExifOrientation(image *C.struct__VipsImage) int { + return int(C.vips_exif_orientation(image)) +} + type vipsSaveOptions struct { Quality int } @@ -132,7 +135,7 @@ func vipsSave(image *C.struct__VipsImage, o vipsSaveOptions) ([]byte, error) { err := C.vips_jpegsave_custom(image, &ptr, &length, 1, C.int(o.Quality), 0) if err != 0 { - return nil, vipsError() + return nil, catchVipsError() } C.g_object_unref(C.gpointer(image)) @@ -143,7 +146,7 @@ func vipsSave(image *C.struct__VipsImage, o vipsSaveOptions) ([]byte, error) { return buf, nil } -func vipsError() error { +func catchVipsError() error { s := C.GoString(C.vips_error_buffer()) C.vips_error_clear() C.vips_thread_shutdown() diff --git a/vips.h b/vips.h index 9cb6f2e..92ab5f9 100644 --- a/vips.h +++ b/vips.h @@ -11,6 +11,12 @@ enum types { MAGICK }; +void +vips_malloc_cb(VipsObject *object, char *buf) +{ + g_free(buf); +}; + int vips_affine_interpolator(VipsImage *in, VipsImage **out, double a, double b, double c, double d, VipsInterpolate *interpolator) { @@ -94,9 +100,27 @@ vips_init_image(void *buf, size_t len, int imageType, VipsImage **out) { #endif } + if (out != NULL) { + // Listen for "postclose" signal to delete input buffer + //g_signal_connect(out, "postclose", G_CALLBACK(vips_malloc_cb), buf); + } + return code; }; +int +vips_exif_orientation(VipsImage *image) { + int orientation = 0; + const char **exif; + if ( + vips_image_get_typeof(image, "exif-ifd0-Orientation") != 0 && + !vips_image_get_string(image, "exif-ifd0-Orientation", exif) + ) { + orientation = atoi(exif[0]); + } + return orientation; +}; + int vips_embed_extend(VipsImage *in, VipsImage **out, int left, int top, int width, int height, int extend) {