@ -8,6 +8,7 @@ import "C"
import (
"errors"
"fmt"
"math"
)
@ -19,7 +20,7 @@ func Resize(buf []byte, o Options) ([]byte, error) {
return nil , err
}
// d efaults
// D efaults
if o . Quality == 0 {
o . Quality = QUALITY
}
@ -36,7 +37,6 @@ func Resize(buf []byte, o Options) ([]byte, error) {
debug ( "Options: %#v" , o )
// get WxH
inWidth := int ( image . Xsize )
inHeight := int ( image . Ysize )
@ -58,7 +58,6 @@ func Resize(buf []byte, o Options) ([]byte, error) {
// Try to use libjpeg shrink-on-load
if imageType == JPEG && shrink >= 2 {
// Recalculate integral shrink and double residual
tmpImage , factor , err := shrinkJpegImage ( buf , factor , shrink )
if err != nil {
return nil , err
@ -78,6 +77,8 @@ func Resize(buf []byte, o Options) ([]byte, error) {
shrink = int ( math . Max ( float64 ( math . Floor ( factor * 3.0 / windowSize ) ) , 1 ) )
}
// Transform image if necessary
if o . Width != inWidth || o . Height != inHeight {
// Use vips_shrink with the integral reduction
if shrink > 1 {
image , residual , err = shrinkImage ( image , o , residual , shrink )
@ -85,7 +86,6 @@ func Resize(buf []byte, o Options) ([]byte, error) {
return nil , err
}
}
// Use vips_affine with the remaining float part
if residual != 0 {
image , err = vipsAffine ( image , residual , o . Interpolator )
@ -93,38 +93,20 @@ func Resize(buf []byte, o Options) ([]byte, error) {
return nil , err
}
}
debug ( "factor: %v, shrink: %v, residual: %v" , factor , shrink , residual )
// Extract image
// Extract area from image
image , err = extractImage ( image , o )
if err != nil {
return nil , err
}
if o . Rotate == 0 {
rotation , flip := calculateRotationAndFlip ( image , o . Rotate )
if flip {
o . Flip = HORIZONTAL
}
if rotation > D0 {
o . Rotate = rotation
}
}
if o . Rotate > 0 {
image , err = vipsRotate ( image , getAngle ( o . Rotate ) )
if err != nil {
return nil , err
}
}
if o . Flip > 0 {
image , err = vipsFlip( image , o . Flip )
// Rotate / flip image if necessary based on EXIF metadata
image , err = rotateImage ( image , o )
if err != nil {
return nil , err
}
}
saveOptions := vipsSaveOptions {
Quality : o . Quality ,
@ -132,6 +114,7 @@ func Resize(buf []byte, o Options) ([]byte, error) {
Compression : o . Compression ,
}
// Finally save as buffer
buf , err = vipsSave ( image , saveOptions )
if err != nil {
return nil , err
@ -142,58 +125,130 @@ func Resize(buf []byte, o Options) ([]byte, error) {
func extractImage ( image * C . struct__VipsImage , o Options ) ( * C . struct__VipsImage , error ) {
var err error = nil
affinedWidth := int ( image . Xsize )
affinedHeight := int ( image . Ysize )
if affinedWidth != o . Width || affinedHeight != o . Height {
width := int ( math . Min ( float64 ( affinedWidth ) , float64 ( o . Width ) ) )
height := int ( math . Min ( float64 ( affinedHeight ) , float64 ( o . Height ) ) )
inWidth := int ( image . Xsize )
inHeight := int ( image . Ysize )
switch {
case o . Crop :
left , top := calculateCrop ( affinedWidth , affinedHeight , o . Width , o . Height , o . Gravity )
width := int ( math . Min ( float64 ( inWidth ) , float64 ( o . Width ) ) )
height := int ( math . Min ( float64 ( inHeight ) , float64 ( o . Height ) ) )
left , top := calculateCrop ( inWidth , inHeight , o . Width , o . Height , o . Gravity )
image , err = vipsExtract ( image , left , top , width , height )
break
case o . Embed :
left , top := ( o . Width - aff ined Width) / 2 , ( o . Height - aff ined Height) / 2
left , top := ( o . Width - inWidth) / 2 , ( o . Height - inHeight) / 2
image , err = vipsEmbed ( image , left , top , o . Width , o . Height , o . Extend )
break
case o . Top > 0 && o . Left > 0 :
image , err = vipsExtract ( image , o . Left , o . Top , width , height )
if o . AreaWidth == 0 || o . AreaHeight == 0 {
err = errors . New ( fmt . Sprintf ( "Invalid area to extract %dx%d" , o . AreaWidth , o . AreaHeight ) )
} else {
image , err = vipsExtract ( image , o . Left , o . Top , o . AreaWidth , o . AreaHeight )
}
break
}
return image , err
}
func rotateImage ( image * C . struct__VipsImage , o Options ) ( * C . struct__VipsImage , error ) {
var err error
rotation , flip := calculateRotationAndFlip ( image , o . Rotate )
if flip {
o . Flip = HORIZONTAL
}
if rotation > D0 && o . Rotate == 0 {
o . Rotate = rotation
}
if o . Rotate > 0 {
image , err = vipsRotate ( image , getAngle ( o . Rotate ) )
}
if o . Flip > 0 {
image , err = vipsFlip ( image , o . Flip )
}
return image , err
}
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 )
if err != nil {
return nil , 0 , err
}
// Recalculate residual float based on dimensions of required vs shrunk images
residualx := float64 ( o . Width ) / float64 ( image . Xsize )
residualy := float64 ( o . Height ) / float64 ( image . Ysize )
if o . Crop {
residual = math . Max ( residualx , residualy )
} else {
residual = math . Min ( residualx , residualy )
}
return image , residual , nil
}
func shrinkJpegImage ( buf [ ] byte , factor float64 , shrink int ) ( * C . struct__VipsImage , float64 , error ) {
var image * C . struct__VipsImage
var err error
shrinkOnLoad := 1
// Recalculate integral shrink and double residual
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
}
// Reload input using shrink-on-load
if shrinkOnLoad > 1 {
image , err = vipsShrinkJpeg ( buf , shrinkOnLoad )
}
return image , factor , err
}
func imageCalculations ( o Options , inWidth , inHeight int ) float64 {
factor := 1.0
xfactor := float64 ( inWidth ) / float64 ( o . Width )
yfactor := float64 ( inHeight ) / float64 ( o . Height )
switch {
// Fixed width and height
case o . Width > 0 && o . Height > 0 :
xf := float64 ( inWidth ) / float64 ( o . Width )
yf := float64 ( inHeight ) / float64 ( o . Height )
if o . Crop {
factor = math . Min ( xf , yf )
factor = math . Min ( xfactor , yfactor )
} else {
factor = math . Max ( xf , yf )
factor = math . Max ( xf actor , yf actor )
}
// Fixed width, auto height
case o . Width > 0 :
factor = float64 ( inWidth ) / float64 ( o . Width )
factor = xfactor
o . Height = int ( math . Floor ( float64 ( inHeight ) / factor ) )
// Fixed height, auto width
case o . Height > 0 :
factor = float64 ( inHeight ) / float64 ( o . Height )
factor = yfactor
o . Width = int ( math . Floor ( float64 ( inWidth ) / factor ) )
// Identity transform
default :
// Identity transform
o . Width = inWidth
o . Height = inHeight
break
}
debug ( "Resolution %dx%d" , o . Width , o . Height )
return factor
}
@ -263,53 +318,6 @@ func calculateRotationAndFlip(image *C.struct__VipsImage, angle Angle) (Angle, b
return rotate , flip
}
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 )
if err != nil {
return nil , 0 , err
}
// Recalculate residual float based on dimensions of required vs shrunk images
residualx := float64 ( o . Width ) / float64 ( image . Xsize )
residualy := float64 ( o . Height ) / float64 ( image . Ysize )
if o . Crop {
residual = math . Max ( residualx , residualy )
} else {
residual = math . Min ( residualx , residualy )
}
return image , residual , nil
}
func shrinkJpegImage ( 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 getAngle ( angle Angle ) Angle {
divisor := angle % 90
if divisor != 0 {