mirror of
https://github.com/talgo-cloud/bimg.git
synced 2026-03-09 23:48:13 -07:00
fix(#46): transform to proper image size
This commit is contained in:
parent
1287087a44
commit
0bff28381f
6 changed files with 100 additions and 53 deletions
|
|
@ -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
|
||||
|
||||
## Supported image operations
|
||||
## Features
|
||||
|
||||
- Resize
|
||||
- Enlarge
|
||||
|
|
@ -52,6 +52,7 @@ For platform specific installations, see [Mac OS](https://github.com/lovell/sha
|
|||
- Thumbnail
|
||||
- Extract area
|
||||
- Watermark (text-based)
|
||||
- Custom output color space (RGB, grayscale...)
|
||||
- Format conversion (with additional quality/compression settings)
|
||||
- EXIF metadata (size, alpha channel, profile, orientation...)
|
||||
|
||||
|
|
@ -65,7 +66,7 @@ Here you can see some performance test comparisons for multiple scenarios:
|
|||
|
||||
#### 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
|
||||
BenchmarkResizePng 20 57592174 ns/op
|
||||
|
|
@ -688,6 +689,7 @@ type Options struct {
|
|||
Embed bool
|
||||
Flip bool
|
||||
Flop bool
|
||||
Force bool
|
||||
NoAutoRotate bool
|
||||
NoProfile bool
|
||||
Interlace bool
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ type Options struct {
|
|||
Embed bool
|
||||
Flip bool
|
||||
Flop bool
|
||||
Force bool
|
||||
NoAutoRotate bool
|
||||
NoProfile bool
|
||||
Interlace bool
|
||||
|
|
|
|||
99
resize.go
99
resize.go
|
|
@ -45,7 +45,7 @@ func Resize(buf []byte, o Options) ([]byte, error) {
|
|||
|
||||
// Do not enlarge the output if the input width or height
|
||||
// are already less than the required dimensions
|
||||
if o.Enlarge == false {
|
||||
if !o.Enlarge && !o.Force {
|
||||
if inWidth < o.Width && inHeight < o.Height {
|
||||
factor = 1.0
|
||||
shrink = 1
|
||||
|
|
@ -81,30 +81,8 @@ func Resize(buf []byte, o Options) ([]byte, error) {
|
|||
}
|
||||
|
||||
// Transform image, if necessary
|
||||
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)
|
||||
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 shouldTransformImage(o, inWidth, inHeight) {
|
||||
image, err = transformImage(image, o, shrink, residual)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -150,16 +128,57 @@ func applyDefaults(o *Options, imageType 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
|
||||
}
|
||||
if !o.Force && !o.Crop && !o.Embed && !o.Enlarge && o.Rotate == 0 && (o.Width > 0 || o.Height > 0) {
|
||||
o.Force = true
|
||||
}
|
||||
}
|
||||
|
||||
func extractImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage, error) {
|
||||
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 extractOrEmbedImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage, error) {
|
||||
var err error = nil
|
||||
inWidth := int(image.Xsize)
|
||||
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)
|
||||
}
|
||||
|
||||
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) {
|
||||
// Use vips_shrink with the integral reduction
|
||||
image, err := vipsShrink(image, shrink)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,46 @@ func TestResize(t *testing.T) {
|
|||
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) {
|
||||
options := Options{Width: 800, Height: 600, Rotate: 270}
|
||||
buf, _ := Read("fixtures/test.jpg")
|
||||
|
|
|
|||
5
vips.go
5
vips.go
|
|
@ -367,15 +367,16 @@ func vipsEmbed(input *C.struct__VipsImage, left, top, width, height, extend int)
|
|||
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
|
||||
cstring := C.CString(i.String())
|
||||
interpolator := C.vips_interpolate_new(cstring)
|
||||
|
||||
defer C.free(unsafe.Pointer(cstring))
|
||||
defer C.g_object_unref(C.gpointer(input))
|
||||
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 {
|
||||
return nil, catchVipsError()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ func TestVipsRotate(t *testing.T) {
|
|||
func TestVipsZoom(t *testing.T) {
|
||||
image, _, _ := vipsRead(readImage("test.jpg"))
|
||||
|
||||
newImg, err := vipsRotate(image, D90)
|
||||
newImg, err := vipsZoom(image, 1)
|
||||
if err != nil {
|
||||
t.Fatal("Cannot save the image")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue