From 9db6f2a0ea6ce5b1ef997b3f2b5d86d022389573 Mon Sep 17 00:00:00 2001 From: Tomas Aparicio Date: Thu, 9 Jul 2015 23:30:08 +0100 Subject: [PATCH] fix(#46): infer resize operation --- resize.go | 52 ++++++++++++++++++++++++++++++++------------------ resize_test.go | 41 +++++++++++++++++++++++++++++++-------- vips.go | 6 ++++-- 3 files changed, 70 insertions(+), 29 deletions(-) diff --git a/resize.go b/resize.go index 30993b4..5769a0e 100644 --- a/resize.go +++ b/resize.go @@ -23,17 +23,6 @@ func Resize(buf []byte, o Options) ([]byte, error) { return nil, err } - // Defaults - if o.Quality == 0 { - o.Quality = QUALITY - } - if o.Compression == 0 { - o.Compression = 6 - } - if o.Type == 0 { - o.Type = imageType - } - if IsTypeSupported(o.Type) == false { return nil, errors.New("Unsupported image output type") } @@ -43,14 +32,20 @@ func Resize(buf []byte, o Options) ([]byte, error) { inWidth := int(image.Xsize) inHeight := int(image.Ysize) + // Define default options + applyDefaults(&o, imageType) + // Infer the required operation based on the in/out image sizes for a coherent transformation + normalizeOperation(&o, inWidth, inHeight) + // image calculations factor := imageCalculations(&o, inWidth, inHeight) shrink := calculateShrink(factor, o.Interpolator) residual := calculateResidual(factor, shrink) - // Do not enlarge the output if the input width *or* height are already less than the required dimensions + // Do not enlarge the output if the input width or height + // are already less than the required dimensions if o.Enlarge == false { - if inWidth < o.Width && inHeight < o.Height { + if inWidth < o.Width || inHeight < o.Height { factor = 1.0 shrink = 1 residual = 0 @@ -88,7 +83,6 @@ func Resize(buf []byte, o Options) ([]byte, error) { transformImage := o.Width != inWidth || o.Height != inHeight || o.AreaWidth > 0 || o.AreaHeight > 0 if transformImage { - // Use vips_shrink with the integral reduction if shrink > 1 { image, residual, err = shrinkImage(image, o, residual, shrink) @@ -138,6 +132,28 @@ func Resize(buf []byte, o Options) ([]byte, error) { return buf, nil } +func applyDefaults(o *Options, imageType ImageType) { + if o.Quality == 0 { + o.Quality = QUALITY + } + if o.Compression == 0 { + o.Compression = 6 + } + if o.Type == 0 { + o.Type = imageType + } +} + +func normalizeOperation(o *Options, inWidth, inHeight int) { + if o.Crop == false && o.Enlarge == false && o.Rotate == 0 && (o.Width > 0 || o.Height > 0) { + if inWidth > o.Width || inHeight > o.Height { + o.Crop = true + } else { + o.Enlarge = true + } + } +} + func extractImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage, error) { var err error = nil inWidth := int(image.Xsize) @@ -239,7 +255,6 @@ func zoomImage(image *C.struct__VipsImage, zoom int) (*C.struct__VipsImage, erro if zoom == 0 { return image, nil } - return vipsZoom(image, zoom+1) } @@ -250,13 +265,12 @@ func affineImage(image *C.struct__VipsImage, o Options, residual float64) (*C.st return nil, err } - if o.Enlarge || (o.Width > int(newImage.Xsize) && o.Height > int(newImage.Ysize)) { + if o.Enlarge || o.Width > 0 || (o.Width > int(newImage.Xsize) && o.Height > int(newImage.Ysize)) { C.g_object_unref(C.gpointer(image)) - image = newImage - } else { - C.g_object_unref(C.gpointer(newImage)) + return newImage, nil } + C.g_object_unref(C.gpointer(newImage)) return image, nil } diff --git a/resize_test.go b/resize_test.go index 7bde1a2..89bb6ae 100644 --- a/resize_test.go +++ b/resize_test.go @@ -20,6 +20,11 @@ func TestResize(t *testing.T) { t.Fatal("Image is not jpeg") } + size, _ := Size(newImg) + if size.Height != options.Height || size.Width != options.Width { + t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height) + } + Write("fixtures/test_out.jpg", newImg) } @@ -36,12 +41,17 @@ func TestRotate(t *testing.T) { t.Fatal("Image is not jpeg") } + size, _ := Size(newImg) + if size.Height != options.Width { + t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height) + } + Write("fixtures/test_rotate_out.jpg", newImg) } -func TestCorruptedImage(t *testing.T) { - options := Options{Width: 800, Height: 600} - buf, _ := Read("fixtures/corrupt.jpg") +func TestInvalidRotate(t *testing.T) { + options := Options{Width: 800, Height: 600, Rotate: 111} + buf, _ := Read("fixtures/test.jpg") newImg, err := Resize(buf, options) if err != nil { @@ -52,12 +62,17 @@ func TestCorruptedImage(t *testing.T) { t.Fatal("Image is not jpeg") } - Write("fixtures/test_corrupt_out.jpg", newImg) + size, _ := Size(newImg) + if size.Height != options.Width { + t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height) + } + + Write("fixtures/test_invalid_rotate_out.jpg", newImg) } -func TestInvalidRotate(t *testing.T) { - options := Options{Width: 800, Height: 600, Rotate: 111} - buf, _ := Read("fixtures/test.jpg") +func TestCorruptedImage(t *testing.T) { + options := Options{Width: 800, Height: 600} + buf, _ := Read("fixtures/corrupt.jpg") newImg, err := Resize(buf, options) if err != nil { @@ -68,7 +83,12 @@ func TestInvalidRotate(t *testing.T) { t.Fatal("Image is not jpeg") } - Write("fixtures/test_invalid_rotate_out.jpg", newImg) + size, _ := Size(newImg) + if size.Height != options.Height || size.Width != options.Width { + t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height) + } + + Write("fixtures/test_corrupt_out.jpg", newImg) } func TestNoColorProfile(t *testing.T) { @@ -84,6 +104,11 @@ func TestNoColorProfile(t *testing.T) { if metadata.Profile == true { t.Fatal("Invalid profile data") } + + size, _ := Size(newImg) + if size.Height != options.Height || size.Width != options.Width { + t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height) + } } func TestConvert(t *testing.T) { diff --git a/vips.go b/vips.go index e190e9c..5a100ce 100644 --- a/vips.go +++ b/vips.go @@ -78,11 +78,12 @@ func Initialize() { C.vips_cache_set_max_mem(maxCacheMem) C.vips_cache_set_max(maxCacheSize) - // Define a custom libvips thread concurrency limit (this may generate thread-unsafe issues) + // Define a custom thread concurrency limit in libvips (this may generate thread-unsafe issues) // See: https://github.com/jcupitt/libvips/issues/261#issuecomment-92850414 if os.Getenv("VIPS_CONCURRENCY") == "" { C.vips_concurrency_set(1) } + // Enable libvips cache tracing if os.Getenv("VIPS_TRACE") != "" { C.vips_enable_cache_set_trace() @@ -91,7 +92,8 @@ func Initialize() { initialized = true } -// Thread-safe function to shutdown libvips. You could call this to drop caches as well. +// Thread-safe function to shutdown libvips. +// You can call this to drop caches as well. // If libvips was already initialized, the function is no-op func Shutdown() { m.Lock()