Skip to content

Commit

Permalink
resources/images: Add images.Mask
Browse files Browse the repository at this point in the history
  • Loading branch information
trickkiste authored and bep committed Jan 11, 2025
1 parent 8af0474 commit 71fae99
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 0 deletions.
8 changes: 8 additions & 0 deletions resources/images/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
63 changes: 63 additions & 0 deletions resources/images/mask.go
Original file line number Diff line number Diff line change
@@ -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())
}

0 comments on commit 71fae99

Please sign in to comment.