187 lines
4.9 KiB
Go
187 lines
4.9 KiB
Go
|
package gl
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"image"
|
||
|
"image/draw"
|
||
|
"math"
|
||
|
|
||
|
"fyne.io/fyne/v2"
|
||
|
"fyne.io/fyne/v2/canvas"
|
||
|
"fyne.io/fyne/v2/internal/cache"
|
||
|
paint "fyne.io/fyne/v2/internal/painter"
|
||
|
"fyne.io/fyne/v2/theme"
|
||
|
)
|
||
|
|
||
|
var noTexture = Texture(cache.NoTexture)
|
||
|
|
||
|
// Texture represents an uploaded GL texture
|
||
|
type Texture cache.TextureType
|
||
|
|
||
|
func (p *painter) freeTexture(obj fyne.CanvasObject) {
|
||
|
texture, ok := cache.GetTexture(obj)
|
||
|
if !ok {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
p.ctx.DeleteTexture(Texture(texture))
|
||
|
p.logError()
|
||
|
cache.DeleteTexture(obj)
|
||
|
}
|
||
|
|
||
|
func (p *painter) getTexture(object fyne.CanvasObject, creator func(canvasObject fyne.CanvasObject) Texture) (Texture, error) {
|
||
|
texture, ok := cache.GetTexture(object)
|
||
|
|
||
|
if !ok {
|
||
|
texture = cache.TextureType(creator(object))
|
||
|
cache.SetTexture(object, texture, p.canvas)
|
||
|
}
|
||
|
if !cache.IsValid(texture) {
|
||
|
return noTexture, fmt.Errorf("no texture available")
|
||
|
}
|
||
|
return Texture(texture), nil
|
||
|
}
|
||
|
|
||
|
func (p *painter) imgToTexture(img image.Image, textureFilter canvas.ImageScale) Texture {
|
||
|
switch i := img.(type) {
|
||
|
case *image.Uniform:
|
||
|
texture := p.newTexture(textureFilter)
|
||
|
r, g, b, a := i.RGBA()
|
||
|
r8, g8, b8, a8 := uint8(r>>8), uint8(g>>8), uint8(b>>8), uint8(a>>8)
|
||
|
data := []uint8{r8, g8, b8, a8}
|
||
|
p.ctx.TexImage2D(
|
||
|
texture2D,
|
||
|
0,
|
||
|
1,
|
||
|
1,
|
||
|
colorFormatRGBA,
|
||
|
unsignedByte,
|
||
|
data,
|
||
|
)
|
||
|
p.logError()
|
||
|
return texture
|
||
|
case *image.RGBA:
|
||
|
if len(i.Pix) == 0 { // image is empty
|
||
|
return noTexture
|
||
|
}
|
||
|
|
||
|
texture := p.newTexture(textureFilter)
|
||
|
p.ctx.TexImage2D(
|
||
|
texture2D,
|
||
|
0,
|
||
|
i.Rect.Size().X,
|
||
|
i.Rect.Size().Y,
|
||
|
colorFormatRGBA,
|
||
|
unsignedByte,
|
||
|
i.Pix,
|
||
|
)
|
||
|
p.logError()
|
||
|
return texture
|
||
|
default:
|
||
|
rgba := image.NewRGBA(image.Rect(0, 0, img.Bounds().Dx(), img.Bounds().Dy()))
|
||
|
draw.Draw(rgba, rgba.Rect, img, image.Point{}, draw.Over)
|
||
|
return p.imgToTexture(rgba, textureFilter)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (p *painter) newGlCircleTexture(obj fyne.CanvasObject) Texture {
|
||
|
circle := obj.(*canvas.Circle)
|
||
|
raw := paint.DrawCircle(circle, paint.VectorPad(circle), p.textureScale)
|
||
|
|
||
|
return p.imgToTexture(raw, canvas.ImageScaleSmooth)
|
||
|
}
|
||
|
|
||
|
func (p *painter) newGlImageTexture(obj fyne.CanvasObject) Texture {
|
||
|
img := obj.(*canvas.Image)
|
||
|
|
||
|
width := p.textureScale(img.Size().Width)
|
||
|
height := p.textureScale(img.Size().Height)
|
||
|
|
||
|
tex := paint.PaintImage(img, p.canvas, int(width), int(height))
|
||
|
if tex == nil {
|
||
|
return noTexture
|
||
|
}
|
||
|
|
||
|
return p.imgToTexture(tex, img.ScaleMode)
|
||
|
}
|
||
|
|
||
|
func (p *painter) newGlLinearGradientTexture(obj fyne.CanvasObject) Texture {
|
||
|
gradient := obj.(*canvas.LinearGradient)
|
||
|
|
||
|
w := gradient.Size().Width
|
||
|
h := gradient.Size().Height
|
||
|
switch gradient.Angle {
|
||
|
case 90, 270:
|
||
|
h = 1
|
||
|
case 0, 180:
|
||
|
w = 1
|
||
|
}
|
||
|
width := p.textureScale(w)
|
||
|
height := p.textureScale(h)
|
||
|
|
||
|
return p.imgToTexture(gradient.Generate(int(width), int(height)), canvas.ImageScaleSmooth)
|
||
|
}
|
||
|
|
||
|
func (p *painter) newGlRadialGradientTexture(obj fyne.CanvasObject) Texture {
|
||
|
gradient := obj.(*canvas.RadialGradient)
|
||
|
|
||
|
width := p.textureScale(gradient.Size().Width)
|
||
|
height := p.textureScale(gradient.Size().Height)
|
||
|
|
||
|
return p.imgToTexture(gradient.Generate(int(width), int(height)), canvas.ImageScaleSmooth)
|
||
|
}
|
||
|
|
||
|
func (p *painter) newGlRasterTexture(obj fyne.CanvasObject) Texture {
|
||
|
rast := obj.(*canvas.Raster)
|
||
|
|
||
|
width := p.textureScale(rast.Size().Width)
|
||
|
height := p.textureScale(rast.Size().Height)
|
||
|
|
||
|
return p.imgToTexture(rast.Generator(int(width), int(height)), rast.ScaleMode)
|
||
|
}
|
||
|
|
||
|
func (p *painter) newGlTextTexture(obj fyne.CanvasObject) Texture {
|
||
|
text := obj.(*canvas.Text)
|
||
|
color := text.Color
|
||
|
if color == nil {
|
||
|
color = theme.ForegroundColor()
|
||
|
}
|
||
|
|
||
|
bounds := text.MinSize()
|
||
|
width := int(math.Ceil(float64(p.textureScale(bounds.Width) + paint.VectorPad(text)))) // potentially italic overspill
|
||
|
height := int(math.Ceil(float64(p.textureScale(bounds.Height))))
|
||
|
img := image.NewNRGBA(image.Rect(0, 0, width, height))
|
||
|
|
||
|
face := paint.CachedFontFace(text.TextStyle, text.TextSize*p.canvas.Scale(), p.texScale)
|
||
|
paint.DrawString(img, text.Text, color, face.Fonts, text.TextSize, p.pixScale, text.TextStyle.TabWidth)
|
||
|
return p.imgToTexture(img, canvas.ImageScaleSmooth)
|
||
|
}
|
||
|
|
||
|
func (p *painter) newTexture(textureFilter canvas.ImageScale) Texture {
|
||
|
if int(textureFilter) >= len(textureFilterToGL) {
|
||
|
fyne.LogError(fmt.Sprintf("Invalid canvas.ImageScale value (%d), using canvas.ImageScaleSmooth as default value", textureFilter), nil)
|
||
|
textureFilter = canvas.ImageScaleSmooth
|
||
|
}
|
||
|
|
||
|
texture := p.ctx.CreateTexture()
|
||
|
p.logError()
|
||
|
p.ctx.ActiveTexture(texture0)
|
||
|
p.ctx.BindTexture(texture2D, texture)
|
||
|
p.logError()
|
||
|
p.ctx.TexParameteri(texture2D, textureMinFilter, textureFilterToGL[textureFilter])
|
||
|
p.ctx.TexParameteri(texture2D, textureMagFilter, textureFilterToGL[textureFilter])
|
||
|
p.ctx.TexParameteri(texture2D, textureWrapS, clampToEdge)
|
||
|
p.ctx.TexParameteri(texture2D, textureWrapT, clampToEdge)
|
||
|
p.logError()
|
||
|
|
||
|
return texture
|
||
|
}
|
||
|
|
||
|
func (p *painter) textureScale(v float32) float32 {
|
||
|
if p.pixScale == 1.0 {
|
||
|
return float32(math.Round(float64(v)))
|
||
|
}
|
||
|
|
||
|
return float32(math.Round(float64(v * p.pixScale)))
|
||
|
}
|