diff --git a/History.md b/History.md index 30df74e..f553f1a 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,9 @@ +## 1.0.5 / 01-10-2016 + +- feat(#92): support Extend param with optional background. +- fix(#106): allow image area extraction without explicit x/y axis. +- feat(api): add Extend type with `libvips` enum alias. + ## 1.0.4 / 29-09-2016 - fix(#111): safe check of magick image type support. diff --git a/fixtures/test_issue.jpg b/fixtures/test_issue.jpg new file mode 100644 index 0000000..8348e38 Binary files /dev/null and b/fixtures/test_issue.jpg differ diff --git a/options.go b/options.go index 4251aab..5920ea7 100644 --- a/options.go +++ b/options.go @@ -112,6 +112,28 @@ const ( InterpretationXYZ Interpretation = C.VIPS_INTERPRETATION_XYZ ) +// Extend represents the image extend mode, used when the edges +// of an image are extended, you can specify how you want the extension done. +// See: http://www.vips.ecs.soton.ac.uk/supported/8.4/doc/html/libvips/libvips-conversion.html#VIPS-EXTEND-BACKGROUND:CAPS +type Extend int + +const ( + // ExtendBlack extend with black (all 0) pixels mode. + ExtendBlack Extend = C.VIPS_EXTEND_BLACK + // ExtendCopy copy the image edges. + ExtendCopy Extend = C.VIPS_EXTEND_COPY + // ExtendRepeat repeat the whole image. + ExtendRepeat Extend = C.VIPS_EXTEND_REPEAT + // ExtendMirror mirror the whole image. + ExtendMirror Extend = C.VIPS_EXTEND_MIRROR + // ExtendWhite extend with white (all bits set) pixels. + ExtendWhite Extend = C.VIPS_EXTEND_WHITE + // ExtendBackground with colour from the background property. + ExtendBackground Extend = C.VIPS_EXTEND_BACKGROUND + // ExtendLast extend with last pixel. + ExtendLast Extend = C.VIPS_EXTEND_LAST +) + // WatermarkFont defines the default watermark font to be used. var WatermarkFont = "sans 10" @@ -159,7 +181,6 @@ type Options struct { AreaWidth int Top int Left int - Extend int Quality int Compression int Zoom int @@ -172,6 +193,7 @@ type Options struct { NoAutoRotate bool NoProfile bool Interlace bool + Extend Extend Rotate Angle Background Color Gravity Gravity diff --git a/resize.go b/resize.go index 4ccc92b..8b5b8df 100644 --- a/resize.go +++ b/resize.go @@ -240,9 +240,9 @@ func extractOrEmbedImage(image *C.VipsImage, o Options) (*C.VipsImage, error) { break case o.Embed: left, top := (o.Width-inWidth)/2, (o.Height-inHeight)/2 - image, err = vipsEmbed(image, left, top, o.Width, o.Height, o.Extend) + image, err = vipsEmbed(image, left, top, o.Width, o.Height, o.Extend, o.Background) break - case o.Top != 0 || o.Left != 0: + case o.Top != 0 || o.Left != 0 || o.AreaWidth != 0 || o.AreaHeight != 0: if o.AreaWidth == 0 { o.AreaHeight = o.Width } diff --git a/resize_test.go b/resize_test.go index aeffa67..98c47f1 100644 --- a/resize_test.go +++ b/resize_test.go @@ -216,6 +216,40 @@ func TestNoColorProfile(t *testing.T) { } } +func TestEmbedExtendColor(t *testing.T) { + options := Options{Width: 400, Height: 600, Crop: false, Embed: true, Extend: ExtendWhite, Background: Color{255, 20, 10}} + buf, _ := Read("fixtures/test_issue.jpg") + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + 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_extend_white_out.jpg", newImg) +} + +func TestEmbedExtendWithCustomColor(t *testing.T) { + options := Options{Width: 400, Height: 600, Crop: false, Embed: true, Extend: 5, Background: Color{255, 20, 10}} + buf, _ := Read("fixtures/test_issue.jpg") + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + 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_extend_background_out.jpg", newImg) +} + func TestGaussianBlur(t *testing.T) { options := Options{Width: 800, Height: 600, GaussianBlur: GaussianBlur{Sigma: 5}} buf, _ := Read("fixtures/test.jpg") @@ -250,6 +284,40 @@ func TestSharpen(t *testing.T) { Write("fixtures/test_sharpen.jpg", newImg) } +func TestExtractWithDefaultAxis(t *testing.T) { + options := Options{AreaWidth: 200, AreaHeight: 200} + buf, _ := Read("fixtures/test.jpg") + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + size, _ := Size(newImg) + if size.Height != options.AreaHeight || size.Width != options.AreaWidth { + t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height) + } + + Write("fixtures/test_extract_defaults.jpg", newImg) +} + +func TestExtractCustomAxis(t *testing.T) { + options := Options{Top: 100, Left: 100, AreaWidth: 200, AreaHeight: 200} + buf, _ := Read("fixtures/test.jpg") + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + size, _ := Size(newImg) + if size.Height != options.AreaHeight || size.Width != options.AreaWidth { + t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height) + } + + Write("fixtures/test_extract_custom_axis.jpg", newImg) +} + func TestConvert(t *testing.T) { width, height := 300, 240 formats := [3]ImageType{PNG, WEBP, JPEG} diff --git a/version.go b/version.go index 238e4a4..99bdc5b 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package bimg // Version represents the current package semantic version. -const Version = "1.0.4" +const Version = "1.0.5" diff --git a/vips.go b/vips.go index a7d3b35..cbf2304 100644 --- a/vips.go +++ b/vips.go @@ -459,11 +459,17 @@ func vipsShrink(input *C.VipsImage, shrink int) (*C.VipsImage, error) { return image, nil } -func vipsEmbed(input *C.VipsImage, left, top, width, height, extend int) (*C.VipsImage, error) { +func vipsEmbed(input *C.VipsImage, left, top, width, height int, extend Extend, background Color) (*C.VipsImage, error) { var image *C.VipsImage - defer C.g_object_unref(C.gpointer(input)) - err := C.vips_embed_bridge(input, &image, C.int(left), C.int(top), C.int(width), C.int(height), C.int(extend)) + // Max extend value, see: http://www.vips.ecs.soton.ac.uk/supported/8.4/doc/html/libvips/libvips-conversion.html#VipsExtend + if extend > 5 { + extend = ExtendBackground + } + + defer C.g_object_unref(C.gpointer(input)) + err := C.vips_embed_bridge(input, &image, C.int(left), C.int(top), C.int(width), + C.int(height), C.int(extend), C.double(background.R), C.double(background.G), C.double(background.B)) if err != 0 { return nil, catchVipsError() } diff --git a/vips.h b/vips.h index 3cbe6cf..371de43 100644 --- a/vips.h +++ b/vips.h @@ -207,7 +207,12 @@ vips_zoom_bridge(VipsImage *in, VipsImage **out, int xfac, int yfac) { } int -vips_embed_bridge(VipsImage *in, VipsImage **out, int left, int top, int width, int height, int extend) { +vips_embed_bridge(VipsImage *in, VipsImage **out, int left, int top, int width, int height, int extend, double r, double g, double b) { + if (extend == VIPS_EXTEND_BACKGROUND) { + double background[3] = {r, g, b}; + VipsArrayDouble *vipsBackground = vips_array_double_new(background, 3); + return vips_embed(in, out, left, top, width, height, "extend", extend, "background", vipsBackground, NULL); + } return vips_embed(in, out, left, top, width, height, "extend", extend, NULL); } @@ -280,7 +285,7 @@ int vips_flatten_background_brigde(VipsImage *in, VipsImage **out, double r, double g, double b) { double background[3] = {r, g, b}; VipsArrayDouble *vipsBackground = vips_array_double_new(background, 3); - + return vips_flatten(in, out, "background", vipsBackground, "max_alpha", vips_is_16bit(in->Type) ? 65535.0 : 255.0,