etk/sprite.go
2024-11-21 11:03:32 -08:00

111 lines
2.6 KiB
Go

package etk
import (
"image"
"github.com/hajimehoshi/ebiten/v2"
)
// Sprite is a resizable image.
type Sprite struct {
*Box
img *ebiten.Image
imgBounds image.Rectangle
thumb *ebiten.Image
thumbBounds image.Rectangle
horizontal Alignment
vertical Alignment
}
// NewSprite returns a new Sprite widget.
func NewSprite(img *ebiten.Image) *Sprite {
return &Sprite{
Box: NewBox(),
img: img,
imgBounds: img.Bounds(),
horizontal: AlignCenter,
vertical: AlignCenter,
}
}
// SetImage sets the image of the Sprite.
func (s *Sprite) SetImage(img *ebiten.Image) {
s.Lock()
defer s.Unlock()
s.img = img
s.thumbBounds = image.Rectangle{}
}
// SetHorizontal sets the horizontal alignment of the Sprite.
func (s *Sprite) SetHorizontal(h Alignment) {
s.Lock()
defer s.Unlock()
s.horizontal = h
s.thumbBounds = image.Rectangle{}
}
// SetVertical sets the vertical alignment of the Sprite.
func (s *Sprite) SetVertical(v Alignment) {
s.Lock()
defer s.Unlock()
s.vertical = v
s.thumbBounds = image.Rectangle{}
}
// Draw draws the Sprite on the screen.
func (s *Sprite) Draw(screen *ebiten.Image) error {
s.Lock()
defer s.Unlock()
if s.rect.Dx() == 0 || s.rect.Dy() == 0 {
return nil // The Sprite has no size. Don't draw anything.
}
op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(float64(s.rect.Min.X), float64(s.rect.Min.Y))
if s.imgBounds.Dx() == s.rect.Dx() && s.imgBounds.Dy() == s.rect.Dy() {
screen.DrawImage(s.img, op)
return nil
} else if s.thumb == nil || s.thumbBounds.Dx() != s.rect.Dx() || s.thumbBounds.Dy() != s.rect.Dy() {
scale, yScale := float64(s.rect.Dx())/float64(s.imgBounds.Dx()), float64(s.rect.Dy())/float64(s.imgBounds.Dy())
if yScale < scale {
scale = yScale
}
thumbOp := &ebiten.DrawImageOptions{}
thumbOp.GeoM.Scale(scale, scale)
if s.horizontal != AlignStart {
delta := float64(s.rect.Dx()) - float64(s.imgBounds.Dx())*scale
if s.horizontal == AlignCenter {
thumbOp.GeoM.Translate(delta/2, 0)
} else { // AlignEnd
thumbOp.GeoM.Translate(delta, 0)
}
}
if s.vertical != AlignStart {
delta := float64(s.rect.Dy()) - float64(s.imgBounds.Dy())*scale
if s.vertical == AlignCenter {
thumbOp.GeoM.Translate(0, delta/2)
} else { // AlignEnd
thumbOp.GeoM.Translate(0, delta)
}
}
createThumb := s.thumb == nil
if !createThumb {
bounds := s.thumb.Bounds()
createThumb = bounds.Dx() != s.rect.Dx() || bounds.Dy() != s.rect.Dy()
}
if createThumb {
s.thumb = ebiten.NewImage(s.rect.Dx(), s.rect.Dy())
}
s.thumb.DrawImage(s.img, thumbOp)
s.thumbBounds = s.thumb.Bounds()
}
screen.DrawImage(s.thumb, op)
return nil
}