]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
resources/images: Add images.Mask
authorMarkus Kienast <mark@trickkiste.at>
Fri, 10 Jan 2025 07:43:51 +0000 (09:43 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sat, 11 Jan 2025 10:20:11 +0000 (12:20 +0200)
See #13244

resources/images/filters.go
resources/images/mask.go [new file with mode: 0644]

index 99fecdf20d773a4e15a2cbf8aa6111599647baad..64776affee598b488e23529b3ffcd5902913cd1d 100644 (file)
@@ -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 (file)
index 0000000..5ce7c5d
--- /dev/null
@@ -0,0 +1,63 @@
+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())
+}