Skip to content

Commit 37aea4b

Browse files
authored
Merge pull request #192 from greut/trim
Add the Trim operation
2 parents b35675c + c1f2667 commit 37aea4b

File tree

8 files changed

+64
-5
lines changed

8 files changed

+64
-5
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ If you're using `gopkg.in`, you can still rely in the `v0` without worrying abou
3535

3636
- Resize
3737
- Enlarge
38-
- Crop (including smart crop support)
38+
- Crop (including smart crop support, libvips 8.5+)
3939
- Rotate (with auto-rotate based on EXIF orientation)
4040
- Flip (with auto-flip based on EXIF metadata)
4141
- Flop
@@ -47,6 +47,7 @@ If you're using `gopkg.in`, you can still rely in the `v0` without worrying abou
4747
- Custom output color space (RGB, grayscale...)
4848
- Format conversion (with additional quality/compression settings)
4949
- EXIF metadata (size, alpha channel, profile, orientation...)
50+
- Trim (libvips 8.6+)
5051

5152
## Prerequisites
5253

fixtures/transparent_trim.png

78.5 KB
Loading

image.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,13 @@ func (i *Image) Colourspace(c Interpretation) ([]byte, error) {
178178
return i.Process(options)
179179
}
180180

181+
// Trim removes the background from the picture. It can result in a 0x0 output
182+
// if the image is all background.
183+
func (i *Image) Trim() ([]byte, error) {
184+
options := Options{Trim: true}
185+
return i.Process(options)
186+
}
187+
181188
// Process processes the image based on the given transformation options,
182189
// talking with libvips bindings accordingly and returning the resultant
183190
// image buffer.

image_test.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,8 +457,8 @@ func TestFluentInterface(t *testing.T) {
457457

458458
func TestImageSmartCrop(t *testing.T) {
459459

460-
if !(VipsMajorVersion >= 8 && VipsMinorVersion > 4) {
461-
t.Skipf("Skipping this test, libvips doesn't meet version requirement %s > 8.4", VipsVersion)
460+
if !(VipsMajorVersion >= 8 && VipsMinorVersion >= 5) {
461+
t.Skipf("Skipping this test, libvips doesn't meet version requirement %s >= 8.5", VipsVersion)
462462
}
463463

464464
i := initImage("northern_cardinal_bird.jpg")
@@ -475,6 +475,26 @@ func TestImageSmartCrop(t *testing.T) {
475475
Write("fixtures/test_smart_crop.jpg", buf)
476476
}
477477

478+
func TestImageTrim(t *testing.T) {
479+
480+
if !(VipsMajorVersion >= 8 && VipsMinorVersion >= 6) {
481+
t.Skipf("Skipping this test, libvips doesn't meet version requirement %s >= 8.6", VipsVersion)
482+
}
483+
484+
i := initImage("transparent.png")
485+
buf, err := i.Trim()
486+
if err != nil {
487+
t.Errorf("Cannot process the image: %#v", err)
488+
}
489+
490+
err = assertSize(buf, 250, 208)
491+
if err != nil {
492+
t.Errorf("The image wasn't trimmed.")
493+
}
494+
495+
Write("fixtures/transparent_trim.png", buf)
496+
}
497+
478498
func TestImageLength(t *testing.T) {
479499
i := initImage("test.jpg")
480500

options.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ type Options struct {
205205
NoProfile bool
206206
Interlace bool
207207
StripMetadata bool
208+
Trim bool
208209
Extend Extend
209210
Rotate Angle
210211
Background Color

resizer.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,8 @@ func normalizeOperation(o *Options, inWidth, inHeight int) {
175175

176176
func shouldTransformImage(o Options, inWidth, inHeight int) bool {
177177
return o.Force || (o.Width > 0 && o.Width != inWidth) ||
178-
(o.Height > 0 && o.Height != inHeight) || o.AreaWidth > 0 || o.AreaHeight > 0
178+
(o.Height > 0 && o.Height != inHeight) || o.AreaWidth > 0 || o.AreaHeight > 0 ||
179+
o.Trim
179180
}
180181

181182
func shouldApplyEffects(o Options) bool {
@@ -268,7 +269,12 @@ func extractOrEmbedImage(image *C.VipsImage, o Options) (*C.VipsImage, error) {
268269
left, top := (o.Width-inWidth)/2, (o.Height-inHeight)/2
269270
image, err = vipsEmbed(image, left, top, o.Width, o.Height, o.Extend, o.Background)
270271
break
271-
272+
case o.Trim:
273+
left, top, width, height, err := vipsTrim(image)
274+
if err == nil {
275+
image, err = vipsExtract(image, left, top, width, height)
276+
}
277+
break
272278
case o.Top != 0 || o.Left != 0 || o.AreaWidth != 0 || o.AreaHeight != 0:
273279
if o.AreaWidth == 0 {
274280
o.AreaHeight = o.Width

vips.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,22 @@ func vipsSmartCrop(image *C.VipsImage, width, height int) (*C.VipsImage, error)
503503
return buf, nil
504504
}
505505

506+
func vipsTrim(image *C.VipsImage) (int, int, int, int, error) {
507+
defer C.g_object_unref(C.gpointer(image))
508+
509+
top := C.int(0)
510+
left := C.int(0)
511+
width := C.int(0)
512+
height := C.int(0)
513+
514+
err := C.vips_find_trim_bridge(image, &top, &left, &width, &height)
515+
if err != 0 {
516+
return 0, 0, 0, 0, catchVipsError()
517+
}
518+
519+
return int(top), int(left), int(width), int(height), nil
520+
}
521+
506522
func vipsShrinkJpeg(buf []byte, input *C.VipsImage, shrink int) (*C.VipsImage, error) {
507523
var image *C.VipsImage
508524
var ptr = unsafe.Pointer(&buf[0])

vips.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,3 +539,11 @@ vips_smartcrop_bridge(VipsImage *in, VipsImage **out, int width, int height) {
539539
return 0;
540540
#endif
541541
}
542+
543+
int vips_find_trim_bridge(VipsImage *in, int *top, int *left, int *width, int *height) {
544+
#if (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 6)
545+
return vips_find_trim(in, top, left, width, height, NULL);
546+
#else
547+
return 0;
548+
#endif
549+
}

0 commit comments

Comments
 (0)