Skip to content

Commit 71fae99

Browse files
trickkistebep
authored andcommitted
resources/images: Add images.Mask
See #13244
1 parent 8af0474 commit 71fae99

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

‎resources/images/filters.go

+8
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ func (*Filters) Overlay(src ImageSource, x, y any) gift.Filter {
5252
}
5353
}
5454

55+
// Mask creates a filter that applies a mask image to the source image.
56+
func (*Filters) Mask(mask ImageSource) gift.Filter {
57+
return filter{
58+
Options: newFilterOpts(mask.Key()),
59+
Filter: maskFilter{mask: mask},
60+
}
61+
}
62+
5563
// Opacity creates a filter that changes the opacity of an image.
5664
// The opacity parameter must be in range (0, 1).
5765
func (*Filters) Opacity(opacity any) gift.Filter {

‎resources/images/mask.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package images
2+
3+
import (
4+
"fmt"
5+
"image"
6+
"image/color"
7+
"image/draw"
8+
9+
"github.com/disintegration/gift"
10+
)
11+
12+
// maskFilter applies a mask image to a base image.
13+
type maskFilter struct {
14+
mask ImageSource
15+
}
16+
17+
// Draw applies the mask to the base image.
18+
func (f maskFilter) Draw(dst draw.Image, baseImage image.Image, options *gift.Options) {
19+
maskImage, err := f.mask.DecodeImage()
20+
if err != nil {
21+
panic(fmt.Sprintf("failed to decode image: %s", err))
22+
}
23+
24+
// Ensure the mask is the same size as the base image
25+
baseBounds := baseImage.Bounds()
26+
maskBounds := maskImage.Bounds()
27+
28+
// Resize mask to match base image size if necessary
29+
if maskBounds.Dx() != baseBounds.Dx() || maskBounds.Dy() != baseBounds.Dy() {
30+
g := gift.New(gift.Resize(baseBounds.Dx(), baseBounds.Dy(), gift.LanczosResampling))
31+
resizedMask := image.NewRGBA(g.Bounds(maskImage.Bounds()))
32+
g.Draw(resizedMask, maskImage)
33+
maskImage = resizedMask
34+
}
35+
36+
// Use gift to convert the resized mask to grayscale
37+
g := gift.New(gift.Grayscale())
38+
grayscaleMask := image.NewGray(g.Bounds(maskImage.Bounds()))
39+
g.Draw(grayscaleMask, maskImage)
40+
41+
// Convert grayscale mask to alpha mask
42+
alphaMask := image.NewAlpha(baseBounds)
43+
for y := baseBounds.Min.Y; y < baseBounds.Max.Y; y++ {
44+
for x := baseBounds.Min.X; x < baseBounds.Max.X; x++ {
45+
grayValue := grayscaleMask.GrayAt(x, y).Y
46+
alphaMask.SetAlpha(x, y, color.Alpha{A: grayValue})
47+
}
48+
}
49+
50+
// Create an RGBA output image
51+
outputImage := image.NewRGBA(baseBounds)
52+
53+
// Apply the mask using draw.DrawMask
54+
draw.DrawMask(outputImage, baseBounds, baseImage, image.Point{}, alphaMask, image.Point{}, draw.Over)
55+
56+
// Copy the result to the destination
57+
gift.New().Draw(dst, outputImage)
58+
}
59+
60+
// Bounds returns the bounds of the resulting image.
61+
func (f maskFilter) Bounds(imgBounds image.Rectangle) image.Rectangle {
62+
return image.Rect(0, 0, imgBounds.Dx(), imgBounds.Dy())
63+
}

0 commit comments

Comments
 (0)