From 5b7fa47256e73a8199a6732a93d0933b05994e09 Mon Sep 17 00:00:00 2001 From: David Byttow Date: Fri, 29 Sep 2017 11:51:36 -0700 Subject: [PATCH 1/6] Adds direct bridge methods --- bridge.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++++-- bridge.go | 176 +++++++++++++++++++++++++++++++++++++++++++++++- bridge.h | 40 ++++++++++- image.go | 96 +++++++------------------- image_test.go | 33 +-------- type.go | 45 ++++--------- util.go | 7 ++ 7 files changed, 435 insertions(+), 144 deletions(-) diff --git a/bridge.c b/bridge.c index 4736f71f..6d3518d1 100644 --- a/bridge.c +++ b/bridge.c @@ -1,7 +1,183 @@ #include "bridge.h" -void SetProperty(VipsObject *object, const char *name, const GValue *value) { +int is_16bit(VipsInterpretation interpretation) { + return interpretation == VIPS_INTERPRETATION_RGB16 || interpretation == VIPS_INTERPRETATION_GREY16; +} + +int init_image(void *buf, size_t len, int imageType, VipsImage **out) { + int code = 1; + + if (imageType == JPEG) { + code = vips_jpegload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL); + } else if (imageType == PNG) { + code = vips_pngload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL); + } else if (imageType == WEBP) { + code = vips_webpload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL); + } else if (imageType == TIFF) { + code = vips_tiffload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL); +#if (VIPS_MAJOR_VERSION >= 8) +#if (VIPS_MINOR_VERSION >= 3) + } else if (imageType == GIF) { + code = vips_gifload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL); + } else if (imageType == PDF) { + code = vips_pdfload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL); + } else if (imageType == SVG) { + code = vips_svgload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL); +#endif + } else if (imageType == MAGICK) { + code = vips_magickload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL); +#endif + } + + return code; +} + +int remove_icc_profile(VipsImage *in) { + return vips_image_remove(in, VIPS_META_ICC_NAME); +} + +int load_jpeg_buffer(void *buf, size_t len, VipsImage **out, int shrink) { + if (shrink > 0) { + return vips_jpegload_buffer(buf, len, out, "shrink", shrink, NULL); + } else { + return vips_jpegload_buffer(buf, len, out, NULL); + } +} + +int save_jpeg_buffer(VipsImage *in, void **buf, size_t *len, int strip, int quality, int interlace) { + return vips_jpegsave_buffer(in, buf, len, + "strip", INT_TO_GBOOLEAN(strip), + "Q", quality, + "optimize_coding", TRUE, + "interlace", INT_TO_GBOOLEAN(interlace), + NULL + ); +} + +int save_png_buffer(VipsImage *in, void **buf, size_t *len, int strip, int compression, int quality, int interlace) { + return vips_pngsave_buffer(in, buf, len, + "strip", INT_TO_GBOOLEAN(strip), + "compression", compression, + "interlace", INT_TO_GBOOLEAN(interlace), + "filter", VIPS_FOREIGN_PNG_FILTER_NONE, + NULL + ); +} + +int save_webp_buffer(VipsImage *in, void **buf, size_t *len, int strip, int quality) { + return vips_webpsave_buffer(in, buf, len, + "strip", INT_TO_GBOOLEAN(strip), + "Q", quality, + NULL + ); +} + +int save_tiff_buffer(VipsImage *in, void **buf, size_t *len) { + return vips_tiffsave_buffer(in, buf, len, NULL); +} + +int is_colorspace_supported(VipsImage *in) { + return vips_colourspace_issupported(in) ? 1 : 0; +} + +int to_colorspace(VipsImage *in, VipsImage **out, VipsInterpretation space) { + return vips_colourspace(in, out, space, NULL); +} + +int flip_image(VipsImage *in, VipsImage **out, int direction) { + return vips_flip(in, out, direction, NULL); +} + +int shrink_image(VipsImage *in, VipsImage **out, double xshrink, double yshrink) { + return vips_shrink(in, out, xshrink, yshrink, NULL); +} + +int reduce_image(VipsImage *in, VipsImage **out, double xshrink, double yshrink) { + return vips_reduce(in, out, xshrink, yshrink, NULL); +} + +int zoom_image(VipsImage *in, VipsImage **out, int xfac, int yfac) { + return vips_zoom(in, out, xfac, yfac, NULL); +} + +int embed_image(VipsImage *in, VipsImage **out, int left, int top, int width, int height, int extend, double r, double g, double b) { + if (extend == VIPS_EXTEND_BACKGROUND) { + double background[3] = {r, g, b}; + VipsArrayDouble *vipsBackground = vips_array_double_new(background, 3); + return vips_embed(in, out, left, top, width, height, "extend", extend, "background", vipsBackground, NULL); + } + return vips_embed(in, out, left, top, width, height, "extend", extend, NULL); +} + +int extract_image_area(VipsImage *in, VipsImage **out, int left, int top, int width, int height) { + return vips_extract_area(in, out, left, top, width, height, NULL); +} + +int flatten_image_background(VipsImage *in, VipsImage **out, double r, double g, double b) { + if (is_16bit(in->Type)) { + r = 65535 * r / 255; + g = 65535 * g / 255; + b = 65535 * b / 255; + } + + double background[3] = {r, g, b}; + VipsArrayDouble *vipsBackground = vips_array_double_new(background, 3); + + return vips_flatten(in, out, + "background", vipsBackground, + "max_alpha", is_16bit(in->Type) ? 65535.0 : 255.0, + NULL + ); +} + +int find_image_loader(int t) { + switch (t) { + case GIF: + return vips_type_find("VipsOperation", "gifload"); + } + if (t == GIF) { + return vips_type_find("VipsOperation", "gifload"); + } else if (t == PDF) { + return vips_type_find("VipsOperation", "pdfload"); + } if (t == TIFF) { + return vips_type_find("VipsOperation", "tiffload"); + } + if (t == SVG) { + return vips_type_find("VipsOperation", "svgload"); + } + if (t == WEBP) { + return vips_type_find("VipsOperation", "webpload"); + } + if (t == PNG) { + return vips_type_find("VipsOperation", "pngload"); + } + if (t == JPEG) { + return vips_type_find("VipsOperation", "jpegload"); + } + if (t == MAGICK) { + return vips_type_find("VipsOperation", "magickload"); + } + return 0; +} + +int find_image_type_saver(int t) { + if (t == TIFF) { + return vips_type_find("VipsOperation", "tiffsave_buffer"); + } + if (t == WEBP) { + return vips_type_find("VipsOperation", "webpsave_buffer"); + } + if (t == PNG) { + return vips_type_find("VipsOperation", "pngsave_buffer"); + } + if (t == JPEG) { + return vips_type_find("VipsOperation", "jpegsave_buffer"); + } + return 0; +} + +void gobject_set_property(VipsObject *object, const char *name, const GValue *value) { VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object ); GType type = G_VALUE_TYPE( value ); @@ -38,7 +214,3 @@ void SetProperty(VipsObject *object, const char *name, const GValue *value) { g_object_set_property( G_OBJECT( object ), name, value ); } } - -int VipsJpegsaveBuffer(VipsImage* in, void **buf, size_t *len, int strip, int quality, int interlace) { - return vips_jpegsave_buffer(in, buf, len, NULL); -} diff --git a/bridge.go b/bridge.go index 8048ce43..76fe6329 100644 --- a/bridge.go +++ b/bridge.go @@ -3,7 +3,15 @@ package vips // #cgo pkg-config: vips // #include "bridge.h" import "C" -import "unsafe" +import ( + "errors" + "fmt" + "unsafe" +) + +const ( + defaultQuality = 80 +) var stringBuffer4096 = fixedString(4096) @@ -119,7 +127,7 @@ func vipsCallOperation(operation *C.VipsOperation, options *Options) error { cName := C.CString(option.Name) defer freeCString(cName) - C.SetProperty( + C.gobject_set_property( (*C.VipsObject)(unsafe.Pointer(operation)), cName, &option.GValue) @@ -153,3 +161,167 @@ func vipsCallOperation(operation *C.VipsOperation, options *Options) error { return nil } + +func vipsPrepareForExport(image *C.VipsImage, options *ExportOptions) (*C.VipsImage, error) { + var outImage *C.VipsImage + + if options.StripProfile { + C.remove_icc_profile(image) + } + + if options.Quality == 0 { + options.Quality = defaultQuality + } + + // Use a default interpretation and cast it to C type + if options.Interpretation == 0 { + options.Interpretation = InterpretationSrgb + } + + interpretation := C.VipsInterpretation(options.Interpretation) + + // Apply the proper colour space + if int(C.is_colorspace_supported(image)) == 1 { + err := C.to_colorspace(image, &outImage, interpretation) + if int(err) != 0 { + return nil, handleVipsError() + } + image = outImage + } + + return image, nil +} + +func vipsLoadFromBuffer(buf []byte) (*C.VipsImage, ImageType, error) { + var image *C.VipsImage + imageType := vipsDetermineImageType(buf) + + if imageType == ImageTypeUnknown { + return nil, ImageTypeUnknown, errors.New("Unsupported image format") + } + + len := C.size_t(len(buf)) + imageBuf := unsafe.Pointer(&buf[0]) + + err := C.init_image(imageBuf, len, C.int(imageType), &image) + if err != 0 { + return nil, ImageTypeUnknown, handleVipsError() + } + + return image, imageType, nil +} + +func vipsExportBuffer(image *C.VipsImage, options *ExportOptions) ([]byte, error) { + tmpImage, err := vipsPrepareForExport(image, options) + if err != nil { + return nil, err + } + + // If these are equal, then we don't want to deref the original image as + // the original will be returned if the target colorspace is not supported + if tmpImage != image { + defer C.g_object_unref(C.gpointer(tmpImage)) + } + + cLen := C.size_t(0) + cErr := C.int(0) + interlaced := C.int(boolToInt(options.Interlaced)) + quality := C.int(options.Quality) + stripMetadata := C.int(boolToInt(options.StripMetadata)) + + if options.Type != ImageTypeUnknown && !IsTypeSupported(options.Type) { + return nil, fmt.Errorf("cannot save to %#v", imageTypes[options.Type]) + } + + var ptr unsafe.Pointer + switch options.Type { + case ImageTypeWEBP: + cErr = C.save_webp_buffer(tmpImage, &ptr, &cLen, stripMetadata, quality) + case ImageTypePNG: + cErr = C.save_png_buffer(tmpImage, &ptr, &cLen, stripMetadata, C.int(options.Compression), quality, interlaced) + case ImageTypeTIFF: + cErr = C.save_tiff_buffer(tmpImage, &ptr, &cLen) + default: + cErr = C.save_jpeg_buffer(tmpImage, &ptr, &cLen, stripMetadata, quality, interlaced) + } + + if int(cErr) != 0 { + return nil, handleVipsError() + } + + buf := C.GoBytes(ptr, C.int(cLen)) + + C.g_free(C.gpointer(ptr)) + C.vips_error_clear() + + return buf, nil +} + +func isTypeSupported(imageType ImageType) bool { + return supportedImageTypes[imageType] +} + +func isColorspaceIsSupportedBuffer(buf []byte) (bool, error) { + image, _, err := vipsLoadFromBuffer(buf) + if err != nil { + return false, err + } + C.g_object_unref(C.gpointer(image)) + return int(C.is_colorspace_supported(image)) == 1, nil +} + +func isColorspaceIsSupported(image *C.VipsImage) bool { + return int(C.is_colorspace_supported(image)) == 1 +} + +func vipsDetermineImageType(buf []byte) ImageType { + if len(buf) < 12 { + return ImageTypeUnknown + } + if buf[0] == 0xFF && buf[1] == 0xD8 && buf[2] == 0xFF { + return ImageTypeJPEG + } + if IsTypeSupported(ImageTypeGIF) && buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46 { + return ImageTypeGIF + } + if buf[0] == 0x89 && buf[1] == 0x50 && buf[2] == 0x4E && buf[3] == 0x47 { + return ImageTypePNG + } + if IsTypeSupported(ImageTypeTIFF) && + ((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) || + (buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A)) { + return ImageTypeTIFF + } + if IsTypeSupported(ImageTypePDF) && buf[0] == 0x25 && buf[1] == 0x50 && buf[2] == 0x44 && buf[3] == 0x46 { + return ImageTypePDF + } + if IsTypeSupported(ImageTypeWEBP) && buf[8] == 0x57 && buf[9] == 0x45 && buf[10] == 0x42 && buf[11] == 0x50 { + return ImageTypeWEBP + } + return ImageTypeUnknown +} + +func vipsShrinkJPEG(buf []byte, input *C.VipsImage, shrink int) (*C.VipsImage, error) { + var image *C.VipsImage + var ptr = unsafe.Pointer(&buf[0]) + defer C.g_object_unref(C.gpointer(input)) + + err := C.load_jpeg_buffer(ptr, C.size_t(len(buf)), &image, C.int(shrink)) + if err != 0 { + return nil, handleVipsError() + } + + return image, nil +} + +func vipsShrink(input *C.VipsImage, shrink int) (*C.VipsImage, error) { + var image *C.VipsImage + defer C.g_object_unref(C.gpointer(input)) + + err := C.shrink_image(input, &image, C.double(float64(shrink)), C.double(float64(shrink))) + if err != 0 { + return nil, handleVipsError() + } + + return image, nil +} diff --git a/bridge.h b/bridge.h index a760c812..20fb7333 100644 --- a/bridge.h +++ b/bridge.h @@ -6,5 +6,41 @@ error_requires_version_8 #endif -void SetProperty(VipsObject* object, const char* name, const GValue* value); -int VipsJpegsaveBuffer(VipsImage* image, void **buf, size_t *len, int strip, int quality, int interlace); +#define INT_TO_GBOOLEAN(bool) (bool > 0 ? TRUE : FALSE) + +enum types { + UNKNOWN = 0, + JPEG, + WEBP, + PNG, + TIFF, + GIF, + PDF, + SVG, + MAGICK +}; + +int init_image(void *buf, size_t len, int imageType, VipsImage **out); +int find_image_type_loader(int t); +int find_image_type_saver(int t); + +int save_jpeg_buffer(VipsImage* image, void **buf, size_t *len, int strip, int quality, int interlace); +int save_png_buffer(VipsImage *in, void **buf, size_t *len, int strip, int compression, int quality, int interlace); +int save_webp_buffer(VipsImage *in, void **buf, size_t *len, int strip, int quality); +int save_tiff_buffer(VipsImage *in, void **buf, size_t *len); +int load_jpeg_buffer(void *buf, size_t len, VipsImage **out, int shrink); + +int to_colorspace(VipsImage *in, VipsImage **out, VipsInterpretation space); +int is_colorspace_supported(VipsImage *in); +int remove_icc_profile(VipsImage *in); + +// Operations +int flip_image(VipsImage *in, VipsImage **out, int direction); +int shrink_image(VipsImage *in, VipsImage **out, double xshrink, double yshrink); +int reduce_image(VipsImage *in, VipsImage **out, double xshrink, double yshrink); +int zoom_image(VipsImage *in, VipsImage **out, int xfac, int yfac); +int embed_image(VipsImage *in, VipsImage **out, int left, int top, int width, int height, int extend, double r, double g, double b); +int extract_image_area(VipsImage *in, VipsImage **out, int left, int top, int width, int height); +int flatten_image_background(VipsImage *in, VipsImage **out, double r, double g, double b); + +void gobject_set_property(VipsObject* object, const char* name, const GValue* value); diff --git a/image.go b/image.go index 8eda09e4..08a180fa 100644 --- a/image.go +++ b/image.go @@ -18,6 +18,16 @@ type Image struct { callEvents []*CallEvent } +type ExportOptions struct { + Type ImageType + Quality int + Compression int + Interlaced bool + StripProfile bool + StripMetadata bool + Interpretation Interpretation +} + // NewImageFromMemory wraps an image around a memory area. The memory area must be a simple // array (e.g., RGBRGBRGB), left-to-right, top-to-bottom. func NewImageFromMemory(bytes []byte, width, height, bands int, format BandFormat) (*Image, error) { @@ -34,49 +44,16 @@ func NewImageFromMemory(bytes []byte, width, height, bands int, format BandForma return newImage(vipsImage), nil } -// NewImageFromFile loads an image buffer from disk and creates a new Image -func NewImageFromFile(path string, opts ...OptionFunc) (*Image, error) { - startupIfNeeded() - fileName, optionString := vipsFilenameSplit8(path) - - operationName, err := vipsForeignFindLoad(fileName) - if err != nil { - return nil, ErrUnsupportedImageFormat - } - - var out *Image - options := NewOptions(opts...).With( - StringInput("filename", fileName), - ImageOutput("out", &out), - ) - - if err := vipsCallString(operationName, options, optionString); err != nil { - return nil, err - } - return out, nil -} - // NewImageFromBuffer loads an image buffer and creates a new Image func NewImageFromBuffer(bytes []byte, opts ...OptionFunc) (*Image, error) { startupIfNeeded() - operationName, err := vipsForeignFindLoadBuffer(bytes) - if err != nil { - return nil, err - } - - var out *Image - blob := NewBlob(bytes) - options := NewOptions(opts...).With( - BlobInput("buffer", blob), - IntInput("access", int(AccessRandom)), - ImageOutput("out", &out), - ) - if err := vipsCall(operationName, options); err != nil { + image, _, err := vipsLoadFromBuffer(bytes) + if err != nil { return nil, err } - return out, nil + return newImage(image), nil } func NewThumbnailFromBuffer(bytes []byte, width int, opts ...OptionFunc) (*Image, error) { @@ -95,7 +72,14 @@ func newImage(vipsImage *C.VipsImage) *Image { } func finalizeImage(i *Image) { - C.g_object_unref(C.gpointer(i.image)) + i.Close() +} + +func (i *Image) Close() { + if i.image != nil { + C.g_object_unref(C.gpointer(i.image)) + i.image = nil + } } // Width returns the width of this image @@ -162,42 +146,8 @@ func (i *Image) ToBytes() ([]byte, error) { } // WriteToBuffer writes the image to a buffer in a format represented by the given suffix (e.g., .jpeg) -func (i *Image) WriteToBuffer(imageType ImageType, opts ...OptionFunc) ([]byte, error) { - startupIfNeeded() - suffix := imageTypeExtensionMap[imageType] - fileName, optionString := vipsFilenameSplit8(suffix) - operationName, err := vipsForeignFindSaveBuffer(fileName) - if err != nil { - return nil, err - } - var blob *Blob - options := NewOptions(opts...).With( - ImageInput("in", i), - BlobOutput("buffer", &blob), - ) - err = vipsCallString(operationName, options, optionString) - if err != nil { - return nil, err - } - if blob != nil { - return blob.ToBytes(), nil - } - return nil, nil -} - -// WriteToFile writes the image to a file on disk based on the format specified in the path -func (i *Image) WriteToFile(path string, opts ...OptionFunc) error { - startupIfNeeded() - fileName, optionString := vipsFilenameSplit8(path) - operationName, err := vipsForeignFindSave(fileName) - if err != nil { - return err - } - options := NewOptions(opts...).With( - ImageInput("in", i), - StringInput("filename", fileName), - ) - return vipsCallString(operationName, options, optionString) +func (i *Image) Export(options ExportOptions) ([]byte, error) { + return vipsExportBuffer(i.image, &options) } type CallEvent struct { diff --git a/image_test.go b/image_test.go index ec24b5cf..41b17376 100644 --- a/image_test.go +++ b/image_test.go @@ -2,7 +2,6 @@ package vips_test import ( "io/ioutil" - "os" "testing" "github.com/davidbyttow/govips" @@ -10,27 +9,6 @@ import ( "github.com/stretchr/testify/require" ) -func TestLoadFromFile(t *testing.T) { - image, err := vips.NewImageFromFile("fixtures/canyon.jpg") - require.Nil(t, err) - assert.Equal(t, 2560, image.Width()) - assert.Equal(t, 1600, image.Height()) -} - -func TestWriteToFile(t *testing.T) { - image, err := vips.NewImageFromFile("fixtures/canyon.jpg") - require.Nil(t, err) - - image = image.Resize(0.25) - - tempDir, err := ioutil.TempDir("", "TestWriteToFile") - require.Nil(t, err) - defer os.RemoveAll(tempDir) - - err = image.WriteToFile(tempDir + "/canyon-out.jpg") - require.Nil(t, err) -} - func TestWriteToBytes(t *testing.T) { buf, err := ioutil.ReadFile("fixtures/canyon.jpg") require.Nil(t, err) @@ -40,7 +18,7 @@ func TestWriteToBytes(t *testing.T) { image = image.Resize(0.25) - buf, err = image.WriteToBuffer(vips.ImageTypeJPEG) + buf, err = image.Export(vips.ExportOptions{}) require.Nil(t, err) assert.True(t, len(buf) > 0) @@ -60,13 +38,6 @@ func TestLoadFromMemory(t *testing.T) { bytes[i*3+2] = 0 } - image, err := vips.NewImageFromMemory(bytes, size, size, 3, vips.BandFormatUchar) - require.Nil(t, err) - - tempDir, err := ioutil.TempDir("", "TestLoadFromMemory") - require.Nil(t, err) - defer os.RemoveAll(tempDir) - - err = image.WriteToFile(tempDir + "red-out.png") + _, err := vips.NewImageFromMemory(bytes, size, size, 3, vips.BandFormatUchar) require.Nil(t, err) } diff --git a/type.go b/type.go index 388f61d8..139ff546 100644 --- a/type.go +++ b/type.go @@ -1,7 +1,7 @@ package vips // #cgo pkg-config: vips -// #include "vips/vips.h" +// #include "bridge.h" import "C" import ( "strings" @@ -13,15 +13,15 @@ type ImageType int // ImageType enum const ( - ImageTypeUnknown ImageType = iota - ImageTypeGIF - ImageTypeJPEG - ImageTypeMagick - ImageTypePDF - ImageTypePNG - ImageTypeSVG - ImageTypeTIFF - ImageTypeWEBP + ImageTypeUnknown ImageType = C.UNKNOWN + ImageTypeGIF ImageType = C.GIF + ImageTypeJPEG ImageType = C.JPEG + ImageTypeMagick ImageType = C.MAGICK + ImageTypePDF ImageType = C.PDF + ImageTypePNG ImageType = C.PNG + ImageTypeSVG ImageType = C.SVG + ImageTypeTIFF ImageType = C.TIFF + ImageTypeWEBP ImageType = C.WEBP ) var imageTypeExtensionMap = map[ImageType]string{ @@ -285,28 +285,11 @@ var ( // DetermineImageType attempts to determine the image type of the given buffer func DetermineImageType(buf []byte) ImageType { - startupIfNeeded() - - size := len(buf) - if size == 0 { - return ImageTypeUnknown - } - - cName := C.vips_foreign_find_load_buffer( - byteArrayPointer(buf), - C.size_t(size)) - - if cName == nil { - return ImageTypeUnknown - } - - imageType := ImageTypeUnknown - name := strings.ToLower(C.GoString(cName)) - if imageType, ok := typeLoaders[name]; ok { - return imageType - } + return vipsDetermineImageType(buf) +} - return imageType +func IsTypeSupported(imageType ImageType) bool { + return supportedImageTypes[imageType] } // InitTypes initializes caches and figures out which image types are supported diff --git a/util.go b/util.go index 5a263ef2..c345b372 100644 --- a/util.go +++ b/util.go @@ -18,6 +18,13 @@ func freeCString(s *C.char) { C.free(unsafe.Pointer(s)) } +func boolToInt(b bool) int { + if b { + return 1 + } + return 0 +} + func toGboolean(b bool) C.gboolean { if b { return C.gboolean(1) From 90684f0b8a3b96796c0fe45651da673f2b283db2 Mon Sep 17 00:00:00 2001 From: David Byttow Date: Fri, 29 Sep 2017 11:55:47 -0700 Subject: [PATCH 2/6] Adds affine transformation --- bridge.c | 4 ++++ bridge.h | 1 + 2 files changed, 5 insertions(+) diff --git a/bridge.c b/bridge.c index 6d3518d1..955d42f2 100644 --- a/bridge.c +++ b/bridge.c @@ -131,6 +131,10 @@ int flatten_image_background(VipsImage *in, VipsImage **out, double r, double g, ); } +int transform_image(VipsImage *in, VipsImage **out, double a, double b, double c, double d, VipsInterpolate *interpolator) { + return vips_affine(in, out, a, b, c, d, "interpolate", interpolator, NULL); +} + int find_image_loader(int t) { switch (t) { case GIF: diff --git a/bridge.h b/bridge.h index 20fb7333..41d8d8ab 100644 --- a/bridge.h +++ b/bridge.h @@ -42,5 +42,6 @@ int zoom_image(VipsImage *in, VipsImage **out, int xfac, int yfac); int embed_image(VipsImage *in, VipsImage **out, int left, int top, int width, int height, int extend, double r, double g, double b); int extract_image_area(VipsImage *in, VipsImage **out, int left, int top, int width, int height); int flatten_image_background(VipsImage *in, VipsImage **out, double r, double g, double b); +int transform_image(VipsImage *in, VipsImage **out, double a, double b, double c, double d, VipsInterpolate *interpolator); void gobject_set_property(VipsObject* object, const char* name, const GValue* value); From 415c788b825fe18b7814318612cc51284fa35ac0 Mon Sep 17 00:00:00 2001 From: David Byttow Date: Fri, 29 Sep 2017 12:03:29 -0700 Subject: [PATCH 3/6] updates to 8.5.8 and go 1.9 --- .travis.yml | 4 ++-- bridge.c | 58 ++++++++++++++++++++++------------------------------- 2 files changed, 26 insertions(+), 36 deletions(-) diff --git a/.travis.yml b/.travis.yml index 570e124f..c3f31171 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,12 +5,12 @@ dist: trusty go: # - 1.6 - - 1.7 + - 1.9 # - tip env: # - LIBVIPS=8.3 - - LIBVIPS=8.4 + - LIBVIPS=8.5.8 # - LIBVIPS=master matrix: diff --git a/bridge.c b/bridge.c index 955d42f2..4aab3db9 100644 --- a/bridge.c +++ b/bridge.c @@ -139,45 +139,35 @@ int find_image_loader(int t) { switch (t) { case GIF: return vips_type_find("VipsOperation", "gifload"); + case PDF: + return vips_type_find("VipsOperation", "pdfload"); + case TIFF: + return vips_type_find("VipsOperation", "tiffload"); + case SVG: + return vips_type_find("VipsOperation", "svgload"); + case WEBP: + return vips_type_find("VipsOperation", "webpload"); + case PNG: + return vips_type_find("VipsOperation", "pngload"); + case JPEG: + return vips_type_find("VipsOperation", "jpegload"); + case MAGICK: + return vips_type_find("VipsOperation", "magickload"); } - if (t == GIF) { - return vips_type_find("VipsOperation", "gifload"); - } else if (t == PDF) { - return vips_type_find("VipsOperation", "pdfload"); - } if (t == TIFF) { - return vips_type_find("VipsOperation", "tiffload"); - } - if (t == SVG) { - return vips_type_find("VipsOperation", "svgload"); - } - if (t == WEBP) { - return vips_type_find("VipsOperation", "webpload"); - } - if (t == PNG) { - return vips_type_find("VipsOperation", "pngload"); - } - if (t == JPEG) { - return vips_type_find("VipsOperation", "jpegload"); - } - if (t == MAGICK) { - return vips_type_find("VipsOperation", "magickload"); - } return 0; } int find_image_type_saver(int t) { - if (t == TIFF) { - return vips_type_find("VipsOperation", "tiffsave_buffer"); - } - if (t == WEBP) { - return vips_type_find("VipsOperation", "webpsave_buffer"); - } - if (t == PNG) { - return vips_type_find("VipsOperation", "pngsave_buffer"); - } - if (t == JPEG) { - return vips_type_find("VipsOperation", "jpegsave_buffer"); - } + switch (t) { + case TIFF: + return vips_type_find("VipsOperation", "tiffsave_buffer"); + case WEBP: + return vips_type_find("VipsOperation", "webpsave_buffer"); + case PNG: + return vips_type_find("VipsOperation", "pngsave_buffer"); + case JPEG: + return vips_type_find("VipsOperation", "jpegsave_buffer"); + } return 0; } From b8dc121f7ad97bad03ee50dd448de98344c617e3 Mon Sep 17 00:00:00 2001 From: David Byttow Date: Fri, 29 Sep 2017 12:07:08 -0700 Subject: [PATCH 4/6] 8.5.8 proper --- .travis.yml | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index c3f31171..d19543ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,19 +4,15 @@ sudo: required dist: trusty go: -# - 1.6 - 1.9 -# - tip env: -# - LIBVIPS=8.3 - - LIBVIPS=8.5.8 -# - LIBVIPS=master + - VIPS_VERSION=8.5.8 matrix: allow_failures: - - env: LIBVIPS=8.2 - - env: LIBVIPS=8.3 + - env: VIPS_VERSION=8.2 + - env: VIPS_VERSION=8.3 cache: apt @@ -39,9 +35,9 @@ addons: # VIPS 8.3.3 requires Poppler 0.30 which is not released on Trusty. before_install: - - wget https://github.com/jcupitt/libvips/archive/$LIBVIPS.zip - - unzip $LIBVIPS - - cd libvips-$LIBVIPS + - wget https://github.com/jcupitt/libvips/archive/v${VIPS_VERSION}.zip + - unzip $VIPS_VERSION + - cd libvips-$VIPS_VERSION - test -f autogen.sh && ./autogen.sh || ./bootstrap.sh - > CXXFLAGS=-D_GLIBCXX_USE_CXX11_ABI=0 From 4aacda9e8059cf446d728a8c4e2c630fed5d30d9 Mon Sep 17 00:00:00 2001 From: David Byttow Date: Fri, 29 Sep 2017 13:16:04 -0700 Subject: [PATCH 5/6] Updates zip location --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d19543ba..11000772 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,8 +36,8 @@ addons: # VIPS 8.3.3 requires Poppler 0.30 which is not released on Trusty. before_install: - wget https://github.com/jcupitt/libvips/archive/v${VIPS_VERSION}.zip - - unzip $VIPS_VERSION - - cd libvips-$VIPS_VERSION + - unzip v${VIPS_VERSION} + - cd libvips-${VIPS_VERSION} - test -f autogen.sh && ./autogen.sh || ./bootstrap.sh - > CXXFLAGS=-D_GLIBCXX_USE_CXX11_ABI=0 From 9ffeab452ab7dc887cad8c69d1b687461d7c0e1d Mon Sep 17 00:00:00 2001 From: David Byttow Date: Fri, 29 Sep 2017 13:24:43 -0700 Subject: [PATCH 6/6] Fixes run tests --- examples/buffer/buffer.go | 10 +++++++--- examples/embed/embed.go | 15 ++++++++++++--- examples/invert/invert.go | 16 ++++++++++++---- examples/resize/resize.go | 12 ++++++++---- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/examples/buffer/buffer.go b/examples/buffer/buffer.go index 245e295d..74aadf11 100644 --- a/examples/buffer/buffer.go +++ b/examples/buffer/buffer.go @@ -23,7 +23,7 @@ func run(inputFile, outputFile string) error { fmt.Printf("Loaded %d x %d pixel image from %s\n", image.Width(), image.Height(), inputFile) - buf, err = image.WriteToBuffer(vips.ImageTypePNG) + buf, err = image.Export(vips.ExportOptions{Type: vips.ImageTypePNG}) if err != nil { return err } @@ -37,10 +37,14 @@ func run(inputFile, outputFile string) error { fmt.Printf("Loaded from memory, %d x %d pixel image\n", image.Width(), image.Height()) - image.WriteToFile(outputFile) + buf, err = image.Export(vips.ExportOptions{}) + if err != nil { + return err + } + err = ioutil.WriteFile(outputFile, buf, 0644) fmt.Printf("Written back to %s\n", outputFile) - return nil + return err } var ( diff --git a/examples/embed/embed.go b/examples/embed/embed.go index c0373ad5..d95bb7c2 100644 --- a/examples/embed/embed.go +++ b/examples/embed/embed.go @@ -3,13 +3,19 @@ package main import ( "flag" "fmt" + "io/ioutil" "os" "github.com/davidbyttow/govips" ) func run(inputFile, outputFile string) error { - in, err := vips.NewImageFromFile(inputFile, + buf, err := ioutil.ReadFile(inputFile) + if err != nil { + return err + } + + in, err := vips.NewImageFromBuffer(buf, vips.IntInput("access", int(vips.AccessSequentialUnbuffered))) if err != nil { return err @@ -18,8 +24,11 @@ func run(inputFile, outputFile string) error { out := in.Embed(10, 10, 1000, 1000, vips.IntInput("extend", int(vips.ExtendCopy))) - out.WriteToFile(outputFile) - return nil + buf, err = out.Export(vips.ExportOptions{}) + if err != nil { + return err + } + return ioutil.WriteFile(outputFile, buf, 0644) } var ( diff --git a/examples/invert/invert.go b/examples/invert/invert.go index 0574ab84..2830b33d 100644 --- a/examples/invert/invert.go +++ b/examples/invert/invert.go @@ -3,13 +3,19 @@ package main import ( "flag" "fmt" + "io/ioutil" "os" "github.com/davidbyttow/govips" ) func run(inputFile, outputFile string) error { - in, err := vips.NewImageFromFile(inputFile, + buf, err := ioutil.ReadFile(inputFile) + if err != nil { + return err + } + + in, err := vips.NewImageFromBuffer(buf, vips.IntInput("access", int(vips.AccessSequentialUnbuffered))) if err != nil { return err @@ -17,9 +23,11 @@ func run(inputFile, outputFile string) error { out := in.Invert() - out.WriteToFile(outputFile) - - return nil + buf, err = out.Export(vips.ExportOptions{}) + if err != nil { + return err + } + return ioutil.WriteFile(outputFile, buf, 0644) } var ( diff --git a/examples/resize/resize.go b/examples/resize/resize.go index c5195465..f65f70f1 100644 --- a/examples/resize/resize.go +++ b/examples/resize/resize.go @@ -3,13 +3,15 @@ package main import ( "flag" "fmt" + "io/ioutil" "os" "github.com/davidbyttow/govips" ) func run(inputFile, outputFile string) error { - in, err := vips.NewImageFromFile(inputFile, + buf, _ := ioutil.ReadFile(inputFile) + in, err := vips.NewImageFromBuffer(buf, vips.IntInput("access", int(vips.AccessSequential))) if err != nil { return err @@ -22,9 +24,11 @@ func run(inputFile, outputFile string) error { out := in.Resize(0.2, vips.InterpolatorInput("interpolate", interp)) - out.WriteToFile(outputFile) - - return nil + buf, err = out.Export(vips.ExportOptions{}) + if err != nil { + return err + } + return ioutil.WriteFile(outputFile, buf, 0644) } var (