package main import ( "image" "image/color" "github.com/hajimehoshi/ebiten/v2" ) const ( GRAD_IDX_TOP = 0 GRAD_IDX_BOTTOM = 1 ) func HexToRGBA(hexcolor int) color.RGBA { return color.RGBA{ R: uint8(hexcolor >> 0x10 & 0xff), G: uint8(hexcolor >> 0x08 & 0xff), B: uint8(hexcolor >> 0x00 & 0xff), A: 0xff, } } type Gradient struct { Scene *ebiten.Image //our resultant image target *image.RGBA //raw pixel array width int height int colors []color.RGBA //gradient boundary colors } func NewGradient(width, height int) *Gradient { return &Gradient{ Scene: ebiten.NewImage(width, height), target: image.NewRGBA(image.Rect(0, 0, width, height)), width: width, height: height, colors: []color.RGBA{HexToRGBA(0xFFFFFF), HexToRGBA(0x000000)}, } } func (g *Gradient) SetColors(top, bottom color.RGBA) { g.colors[GRAD_IDX_TOP] = top g.colors[GRAD_IDX_BOTTOM] = bottom g.fillCurrent() } // performs linear fill on the currently set gradient values, top to bottom (0 to 1) func (g *Gradient) fill(top, bottom color.RGBA) { for y := 0; y < g.height; y++ { //the percent of our transition informs our blending percentIn := float64(y) / float64(g.height) //compute top and bottom blend values as factor of percentage pTopR := float64(top.R) * (1 - percentIn) pTopG := float64(top.G) * (1 - percentIn) pTopB := float64(top.B) * (1 - percentIn) pBottomR := float64(bottom.R) * (percentIn) pBottomG := float64(bottom.G) * (percentIn) pBottomB := float64(bottom.B) * (percentIn) //perform blending on per pixel row basis for x := 0; x < g.width; x++ { pixelIndex := 4 * (g.width*y + x) g.target.Pix[pixelIndex] = uint8(pTopR + pBottomR) g.target.Pix[pixelIndex+1] = uint8(pTopG + pBottomG) g.target.Pix[pixelIndex+2] = uint8(pTopB + pBottomB) g.target.Pix[pixelIndex+3] = 0xff } } g.Scene.WritePixels(g.target.Pix) } // gradient on currently set colors func (g *Gradient) fillCurrent() { g.fill(g.colors[GRAD_IDX_TOP], g.colors[GRAD_IDX_BOTTOM]) }