diff --git a/README.md b/README.md index 4b01a0b..4aa985c 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ bimg was heavily inspired in [sharp](https://github.com/lovell/sharp), a great n ## Prerequisites - [libvips](https://github.com/jcupitt/libvips) v7.40.0+ (7.42.0+ recommended) -- C++11 compatible compiler such as gcc 4.6+ or clang 3.0+ +- C compatible compiler such as gcc 4.6+ or clang 3.0+ ## Installation diff --git a/image.go b/image.go index 4344312..e45c675 100644 --- a/image.go +++ b/image.go @@ -31,6 +31,14 @@ func (i *Image) Crop(width int, height int) ([]byte, error) { return i.Process(options) } +func (i *Image) Thumbnail(width int, height int) ([]byte, error) { + options := Options{ + Width: width, + Height: height, + } + return i.Process(options) +} + func (i *Image) Rotate(a Angle) ([]byte, error) { options := Options{Rotate: a} return i.Process(options) diff --git a/metadata.go b/metadata.go index 08776ba..10c32e7 100644 --- a/metadata.go +++ b/metadata.go @@ -6,8 +6,6 @@ package bimg */ import "C" -import () - type ImageSize struct { Width int Height int @@ -16,9 +14,10 @@ type ImageSize struct { type ImageMetadata struct { Orientation int Alpha bool + Channels int Profile bool - Space int Type string + Space string Size ImageSize } @@ -43,12 +42,19 @@ func Metadata(buf []byte) (ImageMetadata, error) { } defer C.g_object_unref(C.gpointer(image)) + size := ImageSize{ + Width: int(image.Xsize), + Height: int(image.Ysize), + } + metadata := ImageMetadata{ - Type: getImageTypeName(imageType), - Size: ImageSize{ - Width: int(image.Xsize), - Height: int(image.Ysize), - }, + Orientation: vipsExifOrientation(image), + Alpha: vipsHasAlpha(image), + Profile: vipsHasProfile(image), + Space: vipsSpace(image), + Channels: vipsImageBands(image), + Type: getImageTypeName(imageType), + Size: size, } return metadata, nil diff --git a/resize.go b/resize.go index 58e6bbc..2012eda 100644 --- a/resize.go +++ b/resize.go @@ -94,30 +94,17 @@ func Resize(buf []byte, o Options) ([]byte, error) { debug("factor: %v, shrink: %v, residual: %v", factor, shrink, residual) // Try to use libjpeg shrink-on-load - shrinkOnLoad := 1 if imageType == JPEG && shrink >= 2 { - switch { - case shrink >= 8: - factor = factor / 8 - shrinkOnLoad = 8 - case shrink >= 4: - factor = factor / 4 - shrinkOnLoad = 4 - case shrink >= 2: - factor = factor / 2 - shrinkOnLoad = 2 + // Recalculate integral shrink and double residual + tmpImage, factor, err := shrinkJpeg(buf, factor, shrink) + if err != nil { + return nil, err } - - if shrinkOnLoad > 1 { - // Recalculate integral shrink and double residual + if tmpImage != nil { + image = tmpImage factor = math.Max(factor, 1.0) shrink = int(math.Floor(factor)) residual = float64(shrink) / factor - // Reload input using shrink-on-load - image, err = vipsShrinkJpeg(buf, shrinkOnLoad) - if err != nil { - return nil, err - } } } @@ -279,6 +266,33 @@ func calculateRotationAndFlip(image *C.struct__VipsImage, angle Angle) (Angle, b return rotate, flip } +func shrinkJpeg(buf []byte, factor float64, shrink int) (*C.struct__VipsImage, float64, error) { + shrinkOnLoad := 1 + + switch { + case shrink >= 8: + factor = factor / 8 + shrinkOnLoad = 8 + case shrink >= 4: + factor = factor / 4 + shrinkOnLoad = 4 + case shrink >= 2: + factor = factor / 2 + shrinkOnLoad = 2 + } + + if shrinkOnLoad > 1 { + // Reload input using shrink-on-load + image, err := vipsShrinkJpeg(buf, shrinkOnLoad) + if err != nil { + return nil, factor, err + } + return image, factor, err + } + + return nil, factor, nil +} + func calculateRotation(angle Angle) Angle { divisor := angle % 90 if divisor != 0 { diff --git a/vips.go b/vips.go index 60abe26..9e11345 100644 --- a/vips.go +++ b/vips.go @@ -15,8 +15,8 @@ import ( ) var ( - initialized bool = false m sync.Mutex + initialized bool = false ) type vipsImage C.struct__VipsImage @@ -31,7 +31,7 @@ func init() { Initialize() - C.vips_concurrency_set(0) // single-thread + C.vips_concurrency_set(0) // default C.vips_cache_set_max_mem(100 * 1024 * 1024) // 100 MB C.vips_cache_set_max(500) // 500 operations } @@ -49,10 +49,12 @@ func Initialize() { } func Shutdown() { - m.Lock() - defer m.Unlock() - C.vips_shutdown() - initialized = false + if initialized == true { + m.Lock() + defer m.Unlock() + C.vips_shutdown() + initialized = false + } } func vipsRotate(image *C.struct__VipsImage, angle Angle) (*C.struct__VipsImage, error) { @@ -71,7 +73,7 @@ func vipsRotate(image *C.struct__VipsImage, angle Angle) (*C.struct__VipsImage, func vipsFlip(image *C.struct__VipsImage, direction Direction) (*C.struct__VipsImage, error) { var out *C.struct__VipsImage - err := C.vips_flip_custom(image, &out, C.int(direction)) + err := C.vips_flip_bridge(image, &out, C.int(direction)) C.g_object_unref(C.gpointer(image)) if err != 0 { return nil, catchVipsError() @@ -105,7 +107,7 @@ func vipsRead(buf []byte) (*C.struct__VipsImage, ImageType, error) { func vipsExtract(image *C.struct__VipsImage, left, top, width, height int) (*C.struct__VipsImage, error) { var buf *C.struct__VipsImage - err := C.vips_extract_area_custom(image, &buf, C.int(left), C.int(top), C.int(width), C.int(height)) + err := C.vips_extract_area_bridge(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, catchVipsError() @@ -141,7 +143,7 @@ func vipsShrink(input *C.struct__VipsImage, shrink int) (*C.struct__VipsImage, e func vipsEmbed(input *C.struct__VipsImage, left, top, width, height, extend int) (*C.struct__VipsImage, error) { var image *C.struct__VipsImage - err := C.vips_embed_custom(input, &image, C.int(left), C.int(top), C.int(width), C.int(height), C.int(extend)) + err := C.vips_embed_bridge(input, &image, C.int(left), C.int(top), C.int(width), C.int(height), C.int(extend)) C.g_object_unref(C.gpointer(image)) if err != 0 { return nil, catchVipsError() @@ -199,6 +201,26 @@ func vipsExifOrientation(image *C.struct__VipsImage) int { return int(C.vips_exif_orientation(image)) } +func vipsHasAlpha(image *C.struct__VipsImage) bool { + return int(C.has_alpha_channel(image)) > 0 +} + +func vipsHasProfile(image *C.struct__VipsImage) bool { + return int(C.has_profile_embed(image)) > 0 +} + +func vipsWindowSize(name string) int { + return int(C.interpolator_window_size(C.CString(name))) +} + +func vipsSpace(image *C.struct__VipsImage) string { + return C.GoString(C.vips_enum_nick_bridge(image)) +} + +func vipsImageBands(image *C.struct__VipsImage) int { + return int(C.vips_image_bands(image)) +} + type vipsSaveOptions struct { Quality int Compression int @@ -212,13 +234,13 @@ func vipsSave(image *C.struct__VipsImage, o vipsSaveOptions) ([]byte, error) { switch { case o.Type == PNG: - err = C.vips_pngsave_custom(image, &ptr, &length, 1, C.int(o.Compression), C.int(o.Quality), 0) + err = C.vips_pngsave_bridge(image, &ptr, &length, 1, C.int(o.Compression), C.int(o.Quality), 0) break case o.Type == WEBP: - err = C.vips_webpsave_custom(image, &ptr, &length, 1, C.int(o.Quality), 0) + err = C.vips_webpsave_bridge(image, &ptr, &length, 1, C.int(o.Quality), 0) break default: - err = C.vips_jpegsave_custom(image, &ptr, &length, 1, C.int(o.Quality), 0) + err = C.vips_jpegsave_bridge(image, &ptr, &length, 1, C.int(o.Quality), 0) break } diff --git a/vips.h b/vips.h index 94b34ae..16a3aa6 100644 --- a/vips.h +++ b/vips.h @@ -30,7 +30,7 @@ vips_jpegload_buffer_shrink(void *buf, size_t len, VipsImage **out, int shrink) }; int -vips_flip_custom(VipsImage *in, VipsImage **out, int direction) +vips_flip_bridge(VipsImage *in, VipsImage **out, int direction) { return vips_flip(in, out, direction, NULL); }; @@ -71,31 +71,63 @@ vips_exif_orientation(VipsImage *image) { }; int -vips_embed_custom(VipsImage *in, VipsImage **out, int left, int top, int width, int height, int extend) +has_profile_embed(VipsImage *image) { + return (vips_image_get_typeof(image, VIPS_META_ICC_NAME) > 0) ? 1 : 0; +}; + +int +has_alpha_channel(VipsImage *image) { + return ( + (image->Bands == 2 && image->Type == VIPS_INTERPRETATION_B_W) || + (image->Bands == 4 && image->Type != VIPS_INTERPRETATION_CMYK) || + (image->Bands == 5 && image->Type == VIPS_INTERPRETATION_CMYK) + ) ? 1 : 0; +}; + +int +interpolator_window_size(char const *name) { + VipsInterpolate *interpolator = vips_interpolate_new(name); + int window_size = vips_interpolate_get_window_size(interpolator); + g_object_unref(interpolator); + return window_size; +}; + +const char * +vips_enum_nick_bridge(VipsImage *image) { + return vips_enum_nick(VIPS_TYPE_INTERPRETATION, image->Type); +}; + +int +vips_image_bands(VipsImage *image) { + return image->Bands; +}; + +int +vips_embed_bridge(VipsImage *in, VipsImage **out, int left, int top, int width, int height, int extend) { return vips_embed(in, out, left, top, width, height, "extend", extend, NULL); }; int -vips_colourspace_custom(VipsImage *in, VipsImage **out, VipsInterpretation space) +vips_colourspace_bridge(VipsImage *in, VipsImage **out, VipsInterpretation space) { return vips_colourspace(in, out, space, NULL); }; int -vips_extract_area_custom(VipsImage *in, VipsImage **out, int left, int top, int width, int height) +vips_extract_area_bridge(VipsImage *in, VipsImage **out, int left, int top, int width, int height) { return vips_extract_area(in, out, left, top, width, height, NULL); }; int -vips_jpegsave_custom(VipsImage *in, void **buf, size_t *len, int strip, int quality, int interlace) +vips_jpegsave_bridge(VipsImage *in, void **buf, size_t *len, int strip, int quality, int interlace) { return vips_jpegsave_buffer(in, buf, len, "strip", strip, "Q", quality, "optimize_coding", TRUE, "interlace", interlace, NULL); }; int -vips_pngsave_custom(VipsImage *in, void **buf, size_t *len, int strip, int compression, int quality, int interlace) +vips_pngsave_bridge(VipsImage *in, void **buf, size_t *len, int strip, int compression, int quality, int interlace) { #if (VIPS_MAJOR_VERSION >= 8 || (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 42)) return vips_pngsave_buffer(in, buf, len, "strip", FALSE, "compression", compression, @@ -107,7 +139,7 @@ vips_pngsave_custom(VipsImage *in, void **buf, size_t *len, int strip, int compr }; int -vips_webpsave_custom(VipsImage *in, void **buf, size_t *len, int strip, int quality, int interlace) +vips_webpsave_bridge(VipsImage *in, void **buf, size_t *len, int strip, int quality, int interlace) { return vips_webpsave_buffer(in, buf, len, "strip", strip, "Q", quality, "optimize_coding", TRUE, "interlace", interlace, NULL); };