mirror of
https://github.com/talgo-cloud/bimg.git
synced 2026-03-17 03:06:41 -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
|
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
|
||||||
|
|
|
||||||
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
|
// 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 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
|
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")
|
||||||
|
|
|
||||||
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
|
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…
Add table
Add a link
Reference in a new issue