diff --git a/image.go b/image.go index 488582a9..dd7053db 100644 --- a/image.go +++ b/image.go @@ -129,6 +129,21 @@ func (i *Image) Thumbnail(pixels int) ([]byte, error) { return i.Process(options) } +func (i *Image) ThumbnailOptions(o Options) ([]byte, error) { + vipsImage, err := imageThumbnail(i.buffer, o.Width, o.Height, o.NoAutoRotate, o.Crop) + if err != nil { + return nil, err + } + + image, err := saveImage(vipsImage, o) + if err != nil { + return nil, err + } + + i.buffer = image + return image, nil +} + // Watermark adds text as watermark on the given image. func (i *Image) Watermark(w Watermark) ([]byte, error) { options := Options{Watermark: w} diff --git a/image_test.go b/image_test.go index 5af0431d..39ebf2ac 100644 --- a/image_test.go +++ b/image_test.go @@ -212,6 +212,43 @@ func TestImageThumbnail(t *testing.T) { Write("testdata/test_thumbnail_out.jpg", buf) } +func TestImageThumbnailOptions(t *testing.T) { + buf, err := initImage("test.jpg").ThumbnailOptions(Options{ + Height: 100, + Width: 100, + Quality: 50, + Type: JPEG, + }) + if err != nil { + t.Errorf("Cannot process the image: %s", err) + } + + err = assertSize(buf, 100, 62) + if err != nil { + t.Error(err) + } + + Write("testdata/test_thumbnail_options_out.jpg", buf) + + buf, err = initImage("test.jpg").ThumbnailOptions(Options{ + Height: 100, + Width: 100, + Quality: 50, + Type: JPEG, + Crop: true, + }) + if err != nil { + t.Errorf("Cannot process the image: %s", err) + } + + err = assertSize(buf, 100, 100) + if err != nil { + t.Error(err) + } + + Write("testdata/test_thumbnail_options_crop_out.jpg", buf) +} + func TestImageWatermark(t *testing.T) { image := initImage("test.jpg") _, err := image.Crop(800, 600, GravityNorth) diff --git a/resizer.go b/resizer.go index d3bed897..af9e0d52 100644 --- a/resizer.go +++ b/resizer.go @@ -148,6 +148,14 @@ func loadImage(buf []byte) (*C.VipsImage, ImageType, error) { return image, imageType, nil } +func imageThumbnail(buf []byte, width, height int, noRotate, crop bool) (*C.VipsImage, error) { + if len(buf) == 0 { + return nil, errors.New("Image buffer is empty") + } + + return vipsThumbnail(buf, width, height, noRotate, crop) +} + func applyDefaults(o Options, imageType ImageType) Options { if o.Quality == 0 { o.Quality = Quality diff --git a/vips.go b/vips.go index 6a775700..21ec11a7 100644 --- a/vips.go +++ b/vips.go @@ -317,6 +317,25 @@ func vipsRead(buf []byte) (*C.VipsImage, ImageType, error) { return image, imageType, nil } +func vipsThumbnail(buf []byte, width, height int, noAutoRotate, crop bool) (*C.VipsImage, error) { + var image *C.VipsImage + + noRotateParam := C.int(boolToInt(noAutoRotate)) + cropParam := C.int(boolToInt(crop)) + + length := C.size_t(len(buf)) + imageBuf := unsafe.Pointer(&buf[0]) + + err := C.vips_thumbnail_bridge(imageBuf, length, &image, + C.int(width), C.int(height), noRotateParam, cropParam, + ) + if int(err) != 0 { + return nil, catchVipsError() + } + + return image, nil +} + func vipsColourspaceIsSupportedBuffer(buf []byte) (bool, error) { image, _, err := vipsRead(buf) if err != nil { @@ -733,4 +752,4 @@ func vipsGamma(image *C.VipsImage, Gamma float64) (*C.VipsImage, error) { return nil, catchVipsError() } return out, nil -} \ No newline at end of file +} diff --git a/vips.h b/vips.h index 676943da..21e17e49 100644 --- a/vips.h +++ b/vips.h @@ -354,6 +354,23 @@ vips_is_16bit (VipsInterpretation interpretation) { return interpretation == VIPS_INTERPRETATION_RGB16 || interpretation == VIPS_INTERPRETATION_GREY16; } +int +vips_thumbnail_bridge(void *buf, size_t len, VipsImage **out, int width, int height, int no_rotate, int crop) { + if (crop) { + return vips_thumbnail_buffer(buf, len, out, width, + "height", height, + "no_rotate", INT_TO_GBOOLEAN(no_rotate), + "crop", VIPS_INTERESTING_CENTRE, + NULL + ); + } + return vips_thumbnail_buffer(buf, len, out, width, + "height", height, + "no_rotate", INT_TO_GBOOLEAN(no_rotate), + NULL + ); +} + int vips_flatten_background_brigde(VipsImage *in, VipsImage **out, double r, double g, double b) { if (vips_is_16bit(in->Type)) {