Image: defer SDL2 texture loading until Present

master
Noah 2021-07-19 21:23:55 -07:00
parent b87b4825af
commit e7e8b4b2c1
1 changed files with 63 additions and 28 deletions

View File

@ -4,11 +4,13 @@ import (
"fmt" "fmt"
"image" "image"
"image/jpeg" "image/jpeg"
"image/png"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"git.kirsle.net/go/render" "git.kirsle.net/go/render"
"golang.org/x/image/bmp"
) )
// ImageType for supported image formats. // ImageType for supported image formats.
@ -27,8 +29,8 @@ type Image struct {
// Configurable fields for the constructor. // Configurable fields for the constructor.
Type ImageType Type ImageType
Image image.Image Image image.Image // a Go image version
texture render.Texturer texture render.Texturer // (SDL2) Texture, lazy inited on Present.
} }
// NewImage creates a new Image. // NewImage creates a new Image.
@ -47,16 +49,10 @@ func NewImage(c Image) *Image {
} }
// ImageFromImage creates an Image from a Go standard library image.Image. // ImageFromImage creates an Image from a Go standard library image.Image.
func ImageFromImage(e render.Engine, im image.Image) (*Image, error) { func ImageFromImage(im image.Image) (*Image, error) {
tex, err := e.StoreTexture("imgbin.png", im)
if err != nil {
return nil, err
}
return &Image{ return &Image{
Type: PNG, Type: PNG,
Image: im, Image: im,
texture: tex,
}, nil }, nil
} }
@ -69,7 +65,7 @@ func ImageFromTexture(tex render.Texturer) *Image {
} }
// ImageFromFile creates an Image by opening a file from disk. // ImageFromFile creates an Image by opening a file from disk.
func ImageFromFile(e render.Engine, filename string) (*Image, error) { func ImageFromFile(filename string) (*Image, error) {
fh, err := os.Open(filename) fh, err := os.Open(filename)
if err != nil { if err != nil {
return nil, err return nil, err
@ -80,14 +76,8 @@ func ImageFromFile(e render.Engine, filename string) (*Image, error) {
return nil, err return nil, err
} }
tex, err := e.StoreTexture(filename, img)
if err != nil {
return nil, err
}
return &Image{ return &Image{
Image: img, Image: img,
texture: tex,
}, nil }, nil
} }
@ -109,12 +99,34 @@ func OpenImage(e render.Engine, filename string) (*Image, error) {
return nil, fmt.Errorf("OpenImage: %s: not a supported image type", filename) return nil, fmt.Errorf("OpenImage: %s: not a supported image type", filename)
} }
tex, err := e.LoadTexture(filename) // Open the file from disk.
fh, err := os.Open(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
w.texture = tex // Parse it.
switch w.Type {
case PNG:
img, err := png.Decode(fh)
if err != nil {
return nil, err
}
w.Image = img
case JPEG:
img, err := jpeg.Decode(fh)
if err != nil {
return nil, err
}
w.Image = img
case BMP:
img, err := bmp.Decode(fh)
if err != nil {
return nil, err
}
w.Image = img
}
return w, nil return w, nil
} }
@ -131,16 +143,39 @@ func (w *Image) GetRGBA() *image.RGBA {
return rgba return rgba
} }
// Compute the widget. // Size returns the dimensions of the image which is also the widget's size.
func (w *Image) Compute(e render.Engine) { func (w *Image) Size() render.Rect {
w.Resize(w.texture.Size()) if w.Image != nil {
bounds := w.Image.Bounds().Canon()
// Call the BaseWidget Compute in case we have subscribers. return render.Rect{
w.BaseWidget.Compute(e) W: bounds.Max.X,
H: bounds.Max.Y,
}
}
return w.BaseWidget.Size()
} }
// Present the widget. // Counter for unique SDL2 texture names.
var __imageID int
// Present the widget. This should be called on your main thread
// if using SDL2 in case it needs to generate textures.
func (w *Image) Present(e render.Engine, p render.Point) { func (w *Image) Present(e render.Engine, p render.Point) {
// Lazy load the (e.g. SDL2) texture from the stored bitmap.
if w.texture == nil {
if w.Image == nil {
return
}
__imageID++
tex, err := e.StoreTexture(fmt.Sprintf("ui.Image(%d).png", __imageID), w.Image)
if err != nil {
fmt.Printf("ui.Image.Present(): could not make texture: %s\n", err)
return
}
w.texture = tex
}
size := w.texture.Size() size := w.texture.Size()
dst := render.Rect{ dst := render.Rect{
X: p.X, X: p.X,