Masking

Run on your local machine
go run github.com/hajimehoshi/ebiten/v2/examples/masking@latest
Code
package main
import (
"bytes"
"image"
"image/color"
_ "image/jpeg"
"log"
"math"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/examples/resources/images"
)
const (
screenWidth = 320
screenHeight = 240
)
var (
bgImage *ebiten.Image
fgImage *ebiten.Image
maskedFgImage = ebiten.NewImage(screenWidth, screenHeight)
spotLightImage *ebiten.Image
)
func init() {
img, _, err := image.Decode(bytes.NewReader(images.Gophers_jpg))
if err != nil {
log.Fatal(err)
}
bgImage = ebiten.NewImageFromImage(img)
img, _, err = image.Decode(bytes.NewReader(images.FiveYears_jpg))
if err != nil {
log.Fatal(err)
}
fgImage = ebiten.NewImageFromImage(img)
const r = 64
alphas := image.Point{r * 2, r * 2}
a := image.NewAlpha(image.Rectangle{image.ZP, alphas})
for j := 0; j < alphas.Y; j++ {
for i := 0; i < alphas.X; i++ {
d := math.Sqrt(float64((i-r)*(i-r) + (j-r)*(j-r)))
b := uint8(max(0, min(0xff, int(3*d*0xff/r)-2*0xff)))
a.SetAlpha(i, j, color.Alpha{b})
}
}
spotLightImage = ebiten.NewImageFromImage(a)
}
type Game struct {
spotLightX int
spotLightY int
spotLightVX int
spotLightVY int
}
func NewGame() *Game {
return &Game{
spotLightX: 0,
spotLightY: 0,
spotLightVX: 1,
spotLightVY: 1,
}
}
func (g *Game) Update() error {
if g.spotLightVX == 0 {
g.spotLightVX = 1
}
if g.spotLightVY == 0 {
g.spotLightVY = 1
}
g.spotLightX += g.spotLightVX
g.spotLightY += g.spotLightVY
if g.spotLightX < 0 {
g.spotLightX = -g.spotLightX
g.spotLightVX = -g.spotLightVX
}
if g.spotLightY < 0 {
g.spotLightY = -g.spotLightY
g.spotLightVY = -g.spotLightVY
}
s := spotLightImage.Bounds().Size()
maxX, maxY := screenWidth-s.X, screenHeight-s.Y
if maxX < g.spotLightX {
g.spotLightX = -g.spotLightX + 2*maxX
g.spotLightVX = -g.spotLightVX
}
if maxY < g.spotLightY {
g.spotLightY = -g.spotLightY + 2*maxY
g.spotLightVY = -g.spotLightVY
}
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {
maskedFgImage.Fill(color.White)
op := &ebiten.DrawImageOptions{}
op.Blend = ebiten.BlendCopy
op.GeoM.Translate(float64(g.spotLightX), float64(g.spotLightY))
maskedFgImage.DrawImage(spotLightImage, op)
op = &ebiten.DrawImageOptions{}
op.Blend = ebiten.BlendSourceIn
maskedFgImage.DrawImage(fgImage, op)
screen.Fill(color.RGBA{0x00, 0x00, 0x80, 0xff})
screen.DrawImage(bgImage, &ebiten.DrawImageOptions{})
screen.DrawImage(maskedFgImage, &ebiten.DrawImageOptions{})
}
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
return screenWidth, screenHeight
}
func main() {
ebiten.SetWindowSize(screenWidth*2, screenHeight*2)
ebiten.SetWindowTitle("Masking (Ebitengine Demo)")
if err := ebiten.RunGame(&Game{}); err != nil {
log.Fatal(err)
}
}