fix(#46): transform to proper image size

master
Tomas Aparicio 11 years ago
parent 1287087a44
commit 0bff28381f

@ -40,7 +40,7 @@ The [install script](https://github.com/lovell/sharp/blob/master/preinstall.sh)
For platform specific installations, see [Mac OS](https://github.com/lovell/sharp/blob/master/README.md#mac-os-tips) tips or [Windows](https://github.com/lovell/sharp/blob/master/README.md#windows) tips For platform specific installations, see [Mac OS](https://github.com/lovell/sharp/blob/master/README.md#mac-os-tips) tips or [Windows](https://github.com/lovell/sharp/blob/master/README.md#windows) tips
## Supported image operations ## Features
- Resize - Resize
- Enlarge - Enlarge
@ -52,6 +52,7 @@ For platform specific installations, see [Mac OS](https://github.com/lovell/sha
- Thumbnail - Thumbnail
- Extract area - Extract area
- Watermark (text-based) - Watermark (text-based)
- Custom output color space (RGB, grayscale...)
- Format conversion (with additional quality/compression settings) - Format conversion (with additional quality/compression settings)
- EXIF metadata (size, alpha channel, profile, orientation...) - EXIF metadata (size, alpha channel, profile, orientation...)
@ -65,7 +66,7 @@ Here you can see some performance test comparisons for multiple scenarios:
#### Benchmarks #### Benchmarks
Tested using Go 1.4.2 and libvips-7.42.3 in OSX i7 2.7Ghz Tested using Go 1.4.1 and libvips-7.42.3 in OSX i7 2.7Ghz
``` ```
BenchmarkResizeLargeJpeg 50 43400480 ns/op BenchmarkResizeLargeJpeg 50 43400480 ns/op
BenchmarkResizePng 20 57592174 ns/op BenchmarkResizePng 20 57592174 ns/op
@ -688,6 +689,7 @@ type Options struct {
Embed bool Embed bool
Flip bool Flip bool
Flop bool Flop bool
Force bool
NoAutoRotate bool NoAutoRotate bool
NoProfile bool NoProfile bool
Interlace bool Interlace bool

@ -105,6 +105,7 @@ type Options struct {
Embed bool Embed bool
Flip bool Flip bool
Flop bool Flop bool
Force bool
NoAutoRotate bool NoAutoRotate bool
NoProfile bool NoProfile bool
Interlace bool Interlace bool

@ -45,7 +45,7 @@ func Resize(buf []byte, o Options) ([]byte, error) {
// Do not enlarge the output if the input width or height // Do not enlarge the output if the input width or height
// are already less than the required dimensions // are already less than the required dimensions
if o.Enlarge == false { if !o.Enlarge && !o.Force {
if inWidth < o.Width && inHeight < o.Height { if inWidth < o.Width && inHeight < o.Height {
factor = 1.0 factor = 1.0
shrink = 1 shrink = 1
@ -81,30 +81,8 @@ func Resize(buf []byte, o Options) ([]byte, error) {
} }
// Transform image, if necessary // Transform image, if necessary
transformImage := o.Width != inWidth || o.Height != inHeight || o.AreaWidth > 0 || o.AreaHeight > 0 if shouldTransformImage(o, inWidth, inHeight) {
image, err = transformImage(image, o, shrink, residual)
if transformImage {
// Use vips_shrink with the integral reduction
if shrink > 1 {
image, residual, err = shrinkImage(image, o, residual, shrink)
if err != nil {
return nil, err
}
}
debug("Transform: factor=%v, shrink=%v, residual=%v, interpolator=%v",
factor, shrink, residual, o.Interpolator.String())
// Affine with the remaining float part
if residual != 0 {
image, err = affineImage(image, o, residual)
if err != nil {
return nil, err
}
}
// Extract area from image
image, err = extractImage(image, o)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -150,16 +128,57 @@ func applyDefaults(o *Options, imageType ImageType) {
} }
func normalizeOperation(o *Options, inWidth, inHeight int) { func normalizeOperation(o *Options, inWidth, inHeight int) {
if o.Crop == false && o.Enlarge == false && o.Rotate == 0 && (o.Width > 0 || o.Height > 0) { if !o.Force && !o.Crop && !o.Embed && !o.Enlarge && o.Rotate == 0 && (o.Width > 0 || o.Height > 0) {
if inWidth > o.Width || inHeight > o.Height { o.Force = true
o.Crop = true }
} else { }
o.Enlarge = true
func shouldTransformImage(o Options, inWidth, inHeight int) bool {
return o.Force || (o.Width > 0 && o.Width != inWidth) ||
(o.Height > 0 && o.Height != inHeight) || o.AreaWidth > 0 || o.AreaHeight > 0
}
func transformImage(image *C.struct__VipsImage, o Options, shrink int, residual float64) (*C.struct__VipsImage, error) {
var err error
// Use vips_shrink with the integral reduction
if shrink > 1 {
image, residual, err = shrinkImage(image, o, residual, shrink)
if err != nil {
return nil, err
} }
} }
residualx, residualy := residual, residual
if o.Force {
residualx = float64(o.Width) / float64(image.Xsize)
residualy = float64(o.Height) / float64(image.Ysize)
}
if o.Force || residual != 0 {
image, err = vipsAffine(image, residualx, residualy, o.Interpolator)
if err != nil {
return nil, err
}
}
if o.Force {
o.Crop = false
o.Embed = false
}
image, err = extractOrEmbedImage(image, o)
if err != nil {
return nil, err
}
debug("Transform: shrink=%v, residual=%v, interpolator=%v",
shrink, residual, o.Interpolator.String())
return image, nil
} }
func extractImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage, error) { func extractOrEmbedImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage, error) {
var err error = nil var err error = nil
inWidth := int(image.Xsize) inWidth := int(image.Xsize)
inHeight := int(image.Ysize) inHeight := int(image.Ysize)
@ -263,22 +282,6 @@ func zoomImage(image *C.struct__VipsImage, zoom int) (*C.struct__VipsImage, erro
return vipsZoom(image, zoom+1) return vipsZoom(image, zoom+1)
} }
func affineImage(image *C.struct__VipsImage, o Options, residual float64) (*C.struct__VipsImage, error) {
newImage, err := vipsAffine(image, residual, o.Interpolator)
if err != nil {
C.g_object_unref(C.gpointer(image))
return nil, err
}
if o.Enlarge || o.Width > 0 || (o.Width > int(newImage.Xsize) && o.Height > int(newImage.Ysize)) {
C.g_object_unref(C.gpointer(image))
return newImage, nil
}
C.g_object_unref(C.gpointer(newImage))
return image, nil
}
func shrinkImage(image *C.struct__VipsImage, o Options, residual float64, shrink int) (*C.struct__VipsImage, float64, error) { func shrinkImage(image *C.struct__VipsImage, o Options, residual float64, shrink int) (*C.struct__VipsImage, float64, error) {
// Use vips_shrink with the integral reduction // Use vips_shrink with the integral reduction
image, err := vipsShrink(image, shrink) image, err := vipsShrink(image, shrink)

@ -28,6 +28,46 @@ func TestResize(t *testing.T) {
Write("fixtures/test_out.jpg", newImg) Write("fixtures/test_out.jpg", newImg)
} }
func TestResizeCustomSizes(t *testing.T) {
tests := []struct {
file string
format ImageType
options Options
}{
{"test.jpg", JPEG, Options{Width: 800, Height: 600}},
{"test.jpg", JPEG, Options{Width: 1000, Height: 1000}},
{"test.jpg", JPEG, Options{Width: 100, Height: 50}},
{"test.jpg", JPEG, Options{Width: 2000, Height: 2000}},
{"test.jpg", JPEG, Options{Width: 500, Height: 1000}},
{"test.jpg", JPEG, Options{Width: 500}},
{"test.jpg", JPEG, Options{Height: 500}},
{"test.jpg", JPEG, Options{Crop: true, Width: 500, Height: 1000}},
{"test.jpg", JPEG, Options{Crop: true, Enlarge: true, Width: 2000, Height: 1400}},
{"test.jpg", JPEG, Options{Enlarge: true, Force: true, Width: 2000, Height: 2000}},
{"test.jpg", JPEG, Options{Force: true, Width: 2000, Height: 2000}},
}
for _, test := range tests {
buf, _ := Read("fixtures/" + test.file)
image, err := Resize(buf, test.options)
if err != nil {
t.Errorf("Resize(imgData, %#v) error: %#v", test.options, err)
}
if DetermineImageType(image) != test.format {
t.Fatal("Image format is invalid. Expected: %s", test.format)
}
size, _ := Size(image)
if test.options.Height > 0 && size.Height != test.options.Height {
t.Fatalf("Invalid height: %d", size.Height)
}
if test.options.Width > 0 && size.Width != test.options.Width {
t.Fatalf("Invalid width: %d", size.Width)
}
}
}
func TestRotate(t *testing.T) { func TestRotate(t *testing.T) {
options := Options{Width: 800, Height: 600, Rotate: 270} options := Options{Width: 800, Height: 600, Rotate: 270}
buf, _ := Read("fixtures/test.jpg") buf, _ := Read("fixtures/test.jpg")

@ -367,15 +367,16 @@ func vipsEmbed(input *C.struct__VipsImage, left, top, width, height, extend int)
return image, nil return image, nil
} }
func vipsAffine(input *C.struct__VipsImage, residual float64, i Interpolator) (*C.struct__VipsImage, error) { func vipsAffine(input *C.struct__VipsImage, residualx, residualy float64, i Interpolator) (*C.struct__VipsImage, error) {
var image *C.struct__VipsImage var image *C.struct__VipsImage
cstring := C.CString(i.String()) cstring := C.CString(i.String())
interpolator := C.vips_interpolate_new(cstring) interpolator := C.vips_interpolate_new(cstring)
defer C.free(unsafe.Pointer(cstring)) defer C.free(unsafe.Pointer(cstring))
defer C.g_object_unref(C.gpointer(input))
defer C.g_object_unref(C.gpointer(interpolator)) defer C.g_object_unref(C.gpointer(interpolator))
err := C.vips_affine_interpolator(input, &image, C.double(residual), 0, 0, C.double(residual), interpolator) err := C.vips_affine_interpolator(input, &image, C.double(residualx), 0, 0, C.double(residualy), interpolator)
if err != 0 { if err != 0 {
return nil, catchVipsError() return nil, catchVipsError()
} }

@ -58,7 +58,7 @@ func TestVipsRotate(t *testing.T) {
func TestVipsZoom(t *testing.T) { func TestVipsZoom(t *testing.T) {
image, _, _ := vipsRead(readImage("test.jpg")) image, _, _ := vipsRead(readImage("test.jpg"))
newImg, err := vipsRotate(image, D90) newImg, err := vipsZoom(image, 1)
if err != nil { if err != nil {
t.Fatal("Cannot save the image") t.Fatal("Cannot save the image")
} }

Loading…
Cancel
Save