}
}
+// 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 {
--- /dev/null
+package images
+
+import (
+ "fmt"
+ "image"
+ "image/color"
+ "image/draw"
+
+ "github.com/disintegration/gift"
+)
+
+// 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() {
+ 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++ {
+ grayValue := grayscaleMask.GrayAt(x, y).Y
+ alphaMask.SetAlpha(x, y, color.Alpha{A: grayValue})
+ }
+ }
+
+ // 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
+ 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())
+}