From 842f4e3da6e6cc5b684c5ec9d1e7ffd964f95470 Mon Sep 17 00:00:00 2001 From: Markus Kienast Date: Mon, 2 Dec 2024 16:17:39 +0100 Subject: [PATCH 1/5] Adding a mask filter --- go.mod | 1 + go.sum | 3 ++ resources/images/filters.go | 8 +++++ resources/images/mask.go | 60 +++++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 resources/images/mask.go diff --git a/go.mod b/go.mod index a82ecb5f8cc..e1a971d9392 100644 --- a/go.mod +++ b/go.mod @@ -119,6 +119,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect github.com/aws/smithy-go v1.22.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/disintegration/imaging v1.6.2 // indirect github.com/dlclark/regexp2 v1.11.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect diff --git a/go.sum b/go.sum index e60627e688c..11a19f6719a 100644 --- a/go.sum +++ b/go.sum @@ -176,6 +176,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc= github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI= +github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= +github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -518,6 +520,7 @@ golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 h1:QfTh0HpN6hlw6D3vu8DAwC8pB golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.22.0 h1:UtK5yLUzilVrkjMAZAZ34DXGpASN8i8pj8g+O+yd10g= golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4= diff --git a/resources/images/filters.go b/resources/images/filters.go index 0a620716d99..f2ef4407d51 100644 --- a/resources/images/filters.go +++ b/resources/images/filters.go @@ -52,6 +52,14 @@ func (*Filters) Overlay(src ImageSource, x, y any) gift.Filter { } } +// Mask creates a filter that applies a mask image to the source image. +func (*Filters) Mask(mask ImageSource) gift.Filter { + return filter{ + Options: newFilterOpts(mask.Key()), + Filter: maskFilter{mask: mask}, + } +} + // Opacity creates a filter that changes the opacity of an image. // The opacity parameter must be in range (0, 1). func (*Filters) Opacity(opacity any) gift.Filter { diff --git a/resources/images/mask.go b/resources/images/mask.go new file mode 100644 index 00000000000..9872194ec15 --- /dev/null +++ b/resources/images/mask.go @@ -0,0 +1,60 @@ +package images + +import ( + "fmt" + "image" + "image/draw" + "image/color" + + "github.com/disintegration/gift" + "github.com/disintegration/imaging" +) + +var _ gift.Filter = (*overlayFilter)(nil) + +// maskFilter applies a mask image to a base image. +type maskFilter struct { + mask ImageSource +} + +// Draw applies the mask to the base image. +func (f maskFilter) Draw(dst draw.Image, baseImage image.Image, options *gift.Options) { + maskImage, err := f.mask.DecodeImage() + if err != nil { + panic(fmt.Sprintf("failed to decode image: %s", err)) + } + + // Ensure the mask is the same size as the base image + baseBounds := baseImage.Bounds() + maskBounds := maskImage.Bounds() + + + // Resize mask to match base image size if necessary + if maskBounds.Dx() != baseBounds.Dx() || maskBounds.Dy() != baseBounds.Dy() { + maskImage = imaging.Resize(maskImage, baseBounds.Dx(), baseBounds.Dy(), imaging.Lanczos) + } + + alphaMask := image.NewAlpha(baseBounds) + for y := baseBounds.Min.Y; y < baseBounds.Max.Y; y++ { + for x := baseBounds.Min.X; x < baseBounds.Max.X; x++ { + r, g, b, _ := maskImage.At(x, y).RGBA() + brightness := (r + g + b) / 3 // Average RGB to get brightness + alphaMask.SetAlpha(x, y, color.Alpha{A: uint8(brightness >> 8)}) + } + } + + // Create an RGBA output image + outputImage := image.NewRGBA(baseBounds) + + // Apply the mask using draw.DrawMask + draw.DrawMask(outputImage, baseBounds, baseImage, image.Point{}, alphaMask, image.Point{}, draw.Over) + + // Copy the result to the destination + //draw.Draw(dst, dst.Bounds(), outputImage, image.Point{}, draw.Src) + gift.New().Draw(dst, outputImage) +} + +// Bounds returns the bounds of the resulting image. +func (f maskFilter) Bounds(imgBounds image.Rectangle) image.Rectangle { + return image.Rect(0, 0, imgBounds.Dx(), imgBounds.Dy()) +} From 7c04a075fc8cb71bd82f12bf1d0dd347b1be4bd8 Mon Sep 17 00:00:00 2001 From: Markus Kienast Date: Mon, 2 Dec 2024 20:53:03 +0100 Subject: [PATCH 2/5] go.mod --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index e1a971d9392..c9a17d90457 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/clbanning/mxj/v2 v2.7.0 github.com/cli/safeexec v1.0.1 github.com/disintegration/gift v1.2.1 + github.com/disintegration/imaging v1.6.2 github.com/dustin/go-humanize v1.0.1 github.com/evanw/esbuild v0.24.0 github.com/fatih/color v1.18.0 @@ -119,7 +120,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect github.com/aws/smithy-go v1.22.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect - github.com/disintegration/imaging v1.6.2 // indirect github.com/dlclark/regexp2 v1.11.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect From d8f7b1c8be4369c1b49c9957c0d2fefd024a6bd1 Mon Sep 17 00:00:00 2001 From: Markus Kienast Date: Tue, 3 Dec 2024 21:53:50 +0100 Subject: [PATCH 3/5] mask with use of gift instead of imaging mod --- go.mod | 1 - go.sum | 3 --- resources/images/mask.go | 31 ++++++++++++++++++------------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index c9a17d90457..a82ecb5f8cc 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,6 @@ require ( github.com/clbanning/mxj/v2 v2.7.0 github.com/cli/safeexec v1.0.1 github.com/disintegration/gift v1.2.1 - github.com/disintegration/imaging v1.6.2 github.com/dustin/go-humanize v1.0.1 github.com/evanw/esbuild v0.24.0 github.com/fatih/color v1.18.0 diff --git a/go.sum b/go.sum index 11a19f6719a..e60627e688c 100644 --- a/go.sum +++ b/go.sum @@ -176,8 +176,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc= github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI= -github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= -github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -520,7 +518,6 @@ golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 h1:QfTh0HpN6hlw6D3vu8DAwC8pB golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.22.0 h1:UtK5yLUzilVrkjMAZAZ34DXGpASN8i8pj8g+O+yd10g= golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4= diff --git a/resources/images/mask.go b/resources/images/mask.go index 9872194ec15..076af32a8ba 100644 --- a/resources/images/mask.go +++ b/resources/images/mask.go @@ -7,7 +7,6 @@ import ( "image/color" "github.com/disintegration/gift" - "github.com/disintegration/imaging" ) var _ gift.Filter = (*overlayFilter)(nil) @@ -25,21 +24,28 @@ func (f maskFilter) Draw(dst draw.Image, baseImage image.Image, options *gift.Op } // Ensure the mask is the same size as the base image - baseBounds := baseImage.Bounds() - maskBounds := maskImage.Bounds() + baseBounds := baseImage.Bounds() + maskBounds := maskImage.Bounds() - // Resize mask to match base image size if necessary - if maskBounds.Dx() != baseBounds.Dx() || maskBounds.Dy() != baseBounds.Dy() { - maskImage = imaging.Resize(maskImage, baseBounds.Dx(), baseBounds.Dy(), imaging.Lanczos) - } + if maskBounds.Dx() != baseBounds.Dx() || maskBounds.Dy() != baseBounds.Dy() { + g := gift.New(gift.Resize(baseBounds.Dx(), baseBounds.Dy(), gift.LanczosResampling)) + resizedMask := image.NewRGBA(g.Bounds(maskImage.Bounds())) + g.Draw(resizedMask, maskImage) + maskImage = resizedMask + } + + // Use gift to convert the resized mask to grayscale + g := gift.New(gift.Grayscale()) + grayscaleMask := image.NewGray(g.Bounds(maskImage.Bounds())) + g.Draw(grayscaleMask, maskImage) + // Convert grayscale mask to alpha mask alphaMask := image.NewAlpha(baseBounds) for y := baseBounds.Min.Y; y < baseBounds.Max.Y; y++ { for x := baseBounds.Min.X; x < baseBounds.Max.X; x++ { - r, g, b, _ := maskImage.At(x, y).RGBA() - brightness := (r + g + b) / 3 // Average RGB to get brightness - alphaMask.SetAlpha(x, y, color.Alpha{A: uint8(brightness >> 8)}) + grayValue := grayscaleMask.GrayAt(x, y).Y + alphaMask.SetAlpha(x, y, color.Alpha{A: grayValue}) } } @@ -49,9 +55,8 @@ func (f maskFilter) Draw(dst draw.Image, baseImage image.Image, options *gift.Op // Apply the mask using draw.DrawMask draw.DrawMask(outputImage, baseBounds, baseImage, image.Point{}, alphaMask, image.Point{}, draw.Over) - // Copy the result to the destination - //draw.Draw(dst, dst.Bounds(), outputImage, image.Point{}, draw.Src) - gift.New().Draw(dst, outputImage) + // Copy the result to the destination + gift.New().Draw(dst, outputImage) } // Bounds returns the bounds of the resulting image. From 28f1a08a249d1c592434ff120332e8a5b987c8dd Mon Sep 17 00:00:00 2001 From: Markus Kienast Date: Tue, 3 Dec 2024 23:16:30 +0100 Subject: [PATCH 4/5] removed unnecessary var --- resources/images/mask.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/resources/images/mask.go b/resources/images/mask.go index 076af32a8ba..61dc7cb257f 100644 --- a/resources/images/mask.go +++ b/resources/images/mask.go @@ -9,8 +9,6 @@ import ( "github.com/disintegration/gift" ) -var _ gift.Filter = (*overlayFilter)(nil) - // maskFilter applies a mask image to a base image. type maskFilter struct { mask ImageSource From dc5335a0f7f0a134a1e94ca8537049e7f7242590 Mon Sep 17 00:00:00 2001 From: Markus Kienast Date: Fri, 6 Dec 2024 14:32:45 +0100 Subject: [PATCH 5/5] gofmt fixed --- resources/images/filters.go | 8 ++++---- resources/images/mask.go | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/resources/images/filters.go b/resources/images/filters.go index f2ef4407d51..942c63fc9d8 100644 --- a/resources/images/filters.go +++ b/resources/images/filters.go @@ -54,10 +54,10 @@ func (*Filters) Overlay(src ImageSource, x, y any) gift.Filter { // Mask creates a filter that applies a mask image to the source image. func (*Filters) Mask(mask ImageSource) gift.Filter { - return filter{ - Options: newFilterOpts(mask.Key()), - Filter: maskFilter{mask: mask}, - } + return filter{ + Options: newFilterOpts(mask.Key()), + Filter: maskFilter{mask: mask}, + } } // Opacity creates a filter that changes the opacity of an image. diff --git a/resources/images/mask.go b/resources/images/mask.go index 61dc7cb257f..5ce7c5d43e3 100644 --- a/resources/images/mask.go +++ b/resources/images/mask.go @@ -3,15 +3,15 @@ package images import ( "fmt" "image" - "image/draw" "image/color" + "image/draw" "github.com/disintegration/gift" ) // maskFilter applies a mask image to a base image. type maskFilter struct { - mask ImageSource + mask ImageSource } // Draw applies the mask to the base image. @@ -24,7 +24,7 @@ func (f maskFilter) Draw(dst draw.Image, baseImage image.Image, options *gift.Op // Ensure the mask is the same size as the base image baseBounds := baseImage.Bounds() maskBounds := maskImage.Bounds() - + // Resize mask to match base image size if necessary if maskBounds.Dx() != baseBounds.Dx() || maskBounds.Dy() != baseBounds.Dy() { g := gift.New(gift.Resize(baseBounds.Dx(), baseBounds.Dy(), gift.LanczosResampling)) @@ -32,7 +32,7 @@ func (f maskFilter) Draw(dst draw.Image, baseImage image.Image, options *gift.Op g.Draw(resizedMask, maskImage) maskImage = resizedMask } - + // Use gift to convert the resized mask to grayscale g := gift.New(gift.Grayscale()) grayscaleMask := image.NewGray(g.Bounds(maskImage.Bounds())) @@ -59,5 +59,5 @@ func (f maskFilter) Draw(dst draw.Image, baseImage image.Image, options *gift.Op // Bounds returns the bounds of the resulting image. func (f maskFilter) Bounds(imgBounds image.Rectangle) image.Rectangle { - return image.Rect(0, 0, imgBounds.Dx(), imgBounds.Dy()) + return image.Rect(0, 0, imgBounds.Dx(), imgBounds.Dy()) }