From 3ff7fa8b6462d4732ecc6cfd06bc454c36b4f6a0 Mon Sep 17 00:00:00 2001 From: Vetle Leinonen-Roeim Date: Fri, 28 Mar 2025 10:11:43 +0100 Subject: [PATCH] add trim option to image processing and implement trimEdges function --- data.go | 11 ++++++++++- transform.go | 42 ++++++++++++++++++++++++++++++++++++++++++ transform_test.go | 19 +++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/data.go b/data.go index 0545f8b..7f4ffe7 100644 --- a/data.go +++ b/data.go @@ -30,6 +30,7 @@ const ( optCropWidth = "cw" optCropHeight = "ch" optSmartCrop = "sc" + optTrim = "trim" ) // URLError reports a malformed URL error. @@ -80,6 +81,9 @@ type Options struct { // Automatically find good crop points based on image content. SmartCrop bool + + // If true, automatically trim pixels of the same color around the edges + Trim bool } func (o Options) String() string { @@ -123,6 +127,9 @@ func (o Options) String() string { if o.SmartCrop { opts = append(opts, optSmartCrop) } + if o.Trim { + opts = append(opts, optTrim) + } sort.Strings(opts) return strings.Join(opts, ",") } @@ -132,7 +139,7 @@ func (o Options) String() string { // the presence of other fields (like Fit). A non-empty Format value is // assumed to involve a transformation. func (o Options) transform() bool { - return o.Width != 0 || o.Height != 0 || o.Rotate != 0 || o.FlipHorizontal || o.FlipVertical || o.Quality != 0 || o.Format != "" || o.CropX != 0 || o.CropY != 0 || o.CropWidth != 0 || o.CropHeight != 0 + return o.Width != 0 || o.Height != 0 || o.Rotate != 0 || o.FlipHorizontal || o.FlipVertical || o.Quality != 0 || o.Format != "" || o.CropX != 0 || o.CropY != 0 || o.CropWidth != 0 || o.CropHeight != 0 || o.Trim } // ParseOptions parses str as a list of comma separated transformation options. @@ -251,6 +258,8 @@ func ParseOptions(str string) Options { options.Format = opt case opt == optSmartCrop: options.SmartCrop = true + case opt == optTrim: + options.Trim = true case strings.HasPrefix(opt, optRotatePrefix): value := strings.TrimPrefix(opt, optRotatePrefix) options.Rotate, _ = strconv.Atoi(value) diff --git a/transform.go b/transform.go index 103e20f..b9d90c9 100644 --- a/transform.go +++ b/transform.go @@ -309,5 +309,47 @@ func transformImage(m image.Image, opt Options) image.Image { m = imaging.FlipH(m) } + // trim + if opt.Trim { + m = trimEdges(m) + } + return m } + +func trimEdges(img image.Image) image.Image { + bounds := img.Bounds() + minX, minY, maxX, maxY := bounds.Max.X, bounds.Max.Y, bounds.Min.X, bounds.Min.Y + + // Get the color of the first pixel (top-left corner) + baseColor := img.At(bounds.Min.X, bounds.Min.Y) + + // Check each pixel and find the bounding box of non-matching pixels + for y := bounds.Min.Y; y < bounds.Max.Y; y++ { + for x := bounds.Min.X; x < bounds.Max.X; x++ { + if img.At(x, y) != baseColor { // Non-matching pixel + if x < minX { + minX = x + } + if y < minY { + minY = y + } + if x > maxX { + maxX = x + } + if y > maxY { + maxY = y + } + } + } + } + + // If no non-matching pixels are found, return the original image + if minX > maxX || minY > maxY { + return img + } + + // Crop the image to the bounding box of non-matching pixels + croppedImg := imaging.Crop(img, image.Rect(minX, minY, maxX+1, maxY+1)) + return croppedImg +} diff --git a/transform_test.go b/transform_test.go index b22afab..b130ea4 100644 --- a/transform_test.go +++ b/transform_test.go @@ -375,3 +375,22 @@ func TestTransformImage(t *testing.T) { } } } + +func TestTrimBordersOfSameColor(t *testing.T) { + src := newImage(4, 4, + color.NRGBA{255, 255, 255, 255}, color.NRGBA{255, 255, 255, 255}, color.NRGBA{255, 255, 255, 255}, color.NRGBA{255, 255, 255, 255}, + color.NRGBA{255, 255, 255, 255}, color.NRGBA{255, 0, 0, 255}, color.NRGBA{255, 0, 0, 255}, color.NRGBA{255, 255, 255, 255}, + color.NRGBA{255, 255, 255, 255}, color.NRGBA{255, 0, 0, 255}, color.NRGBA{255, 0, 0, 255}, color.NRGBA{255, 255, 255, 255}, + color.NRGBA{255, 255, 255, 255}, color.NRGBA{255, 255, 255, 255}, color.NRGBA{255, 255, 255, 255}, color.NRGBA{255, 255, 255, 255}, + ) + + want := newImage(2, 2, + color.NRGBA{255, 0, 0, 255}, color.NRGBA{255, 0, 0, 255}, + color.NRGBA{255, 0, 0, 255}, color.NRGBA{255, 0, 0, 255}, + ) + + got := trimEdges(src) + if !reflect.DeepEqual(got, want) { + t.Errorf("trimEdges() = %v, want %v", got, want) + } +}