texture.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. package gl
  2. import (
  3. "fmt"
  4. "image"
  5. "image/draw"
  6. "math"
  7. "fyne.io/fyne/v2"
  8. "fyne.io/fyne/v2/canvas"
  9. "fyne.io/fyne/v2/internal/cache"
  10. paint "fyne.io/fyne/v2/internal/painter"
  11. "fyne.io/fyne/v2/theme"
  12. )
  13. var noTexture = Texture(cache.NoTexture)
  14. // Texture represents an uploaded GL texture
  15. type Texture cache.TextureType
  16. func (p *painter) freeTexture(obj fyne.CanvasObject) {
  17. texture, ok := cache.GetTexture(obj)
  18. if !ok {
  19. return
  20. }
  21. p.ctx.DeleteTexture(Texture(texture))
  22. p.logError()
  23. cache.DeleteTexture(obj)
  24. }
  25. func (p *painter) getTexture(object fyne.CanvasObject, creator func(canvasObject fyne.CanvasObject) Texture) (Texture, error) {
  26. texture, ok := cache.GetTexture(object)
  27. if !ok {
  28. texture = cache.TextureType(creator(object))
  29. cache.SetTexture(object, texture, p.canvas)
  30. }
  31. if !cache.IsValid(texture) {
  32. return noTexture, fmt.Errorf("no texture available")
  33. }
  34. return Texture(texture), nil
  35. }
  36. func (p *painter) imgToTexture(img image.Image, textureFilter canvas.ImageScale) Texture {
  37. switch i := img.(type) {
  38. case *image.Uniform:
  39. texture := p.newTexture(textureFilter)
  40. r, g, b, a := i.RGBA()
  41. r8, g8, b8, a8 := uint8(r>>8), uint8(g>>8), uint8(b>>8), uint8(a>>8)
  42. data := []uint8{r8, g8, b8, a8}
  43. p.ctx.TexImage2D(
  44. texture2D,
  45. 0,
  46. 1,
  47. 1,
  48. colorFormatRGBA,
  49. unsignedByte,
  50. data,
  51. )
  52. p.logError()
  53. return texture
  54. case *image.RGBA:
  55. if len(i.Pix) == 0 { // image is empty
  56. return noTexture
  57. }
  58. texture := p.newTexture(textureFilter)
  59. p.ctx.TexImage2D(
  60. texture2D,
  61. 0,
  62. i.Rect.Size().X,
  63. i.Rect.Size().Y,
  64. colorFormatRGBA,
  65. unsignedByte,
  66. i.Pix,
  67. )
  68. p.logError()
  69. return texture
  70. default:
  71. rgba := image.NewRGBA(image.Rect(0, 0, img.Bounds().Dx(), img.Bounds().Dy()))
  72. draw.Draw(rgba, rgba.Rect, img, image.Point{}, draw.Over)
  73. return p.imgToTexture(rgba, textureFilter)
  74. }
  75. }
  76. func (p *painter) newGlCircleTexture(obj fyne.CanvasObject) Texture {
  77. circle := obj.(*canvas.Circle)
  78. raw := paint.DrawCircle(circle, paint.VectorPad(circle), p.textureScale)
  79. return p.imgToTexture(raw, canvas.ImageScaleSmooth)
  80. }
  81. func (p *painter) newGlImageTexture(obj fyne.CanvasObject) Texture {
  82. img := obj.(*canvas.Image)
  83. width := p.textureScale(img.Size().Width)
  84. height := p.textureScale(img.Size().Height)
  85. tex := paint.PaintImage(img, p.canvas, int(width), int(height))
  86. if tex == nil {
  87. return noTexture
  88. }
  89. return p.imgToTexture(tex, img.ScaleMode)
  90. }
  91. func (p *painter) newGlLinearGradientTexture(obj fyne.CanvasObject) Texture {
  92. gradient := obj.(*canvas.LinearGradient)
  93. w := gradient.Size().Width
  94. h := gradient.Size().Height
  95. switch gradient.Angle {
  96. case 90, 270:
  97. h = 1
  98. case 0, 180:
  99. w = 1
  100. }
  101. width := p.textureScale(w)
  102. height := p.textureScale(h)
  103. return p.imgToTexture(gradient.Generate(int(width), int(height)), canvas.ImageScaleSmooth)
  104. }
  105. func (p *painter) newGlRadialGradientTexture(obj fyne.CanvasObject) Texture {
  106. gradient := obj.(*canvas.RadialGradient)
  107. width := p.textureScale(gradient.Size().Width)
  108. height := p.textureScale(gradient.Size().Height)
  109. return p.imgToTexture(gradient.Generate(int(width), int(height)), canvas.ImageScaleSmooth)
  110. }
  111. func (p *painter) newGlRasterTexture(obj fyne.CanvasObject) Texture {
  112. rast := obj.(*canvas.Raster)
  113. width := p.textureScale(rast.Size().Width)
  114. height := p.textureScale(rast.Size().Height)
  115. return p.imgToTexture(rast.Generator(int(width), int(height)), rast.ScaleMode)
  116. }
  117. func (p *painter) newGlTextTexture(obj fyne.CanvasObject) Texture {
  118. text := obj.(*canvas.Text)
  119. color := text.Color
  120. if color == nil {
  121. color = theme.ForegroundColor()
  122. }
  123. bounds := text.MinSize()
  124. width := int(math.Ceil(float64(p.textureScale(bounds.Width) + paint.VectorPad(text)))) // potentially italic overspill
  125. height := int(math.Ceil(float64(p.textureScale(bounds.Height))))
  126. img := image.NewNRGBA(image.Rect(0, 0, width, height))
  127. face := paint.CachedFontFace(text.TextStyle, text.TextSize*p.canvas.Scale(), p.texScale)
  128. paint.DrawString(img, text.Text, color, face.Fonts, text.TextSize, p.pixScale, text.TextStyle.TabWidth)
  129. return p.imgToTexture(img, canvas.ImageScaleSmooth)
  130. }
  131. func (p *painter) newTexture(textureFilter canvas.ImageScale) Texture {
  132. if int(textureFilter) >= len(textureFilterToGL) {
  133. fyne.LogError(fmt.Sprintf("Invalid canvas.ImageScale value (%d), using canvas.ImageScaleSmooth as default value", textureFilter), nil)
  134. textureFilter = canvas.ImageScaleSmooth
  135. }
  136. texture := p.ctx.CreateTexture()
  137. p.logError()
  138. p.ctx.ActiveTexture(texture0)
  139. p.ctx.BindTexture(texture2D, texture)
  140. p.logError()
  141. p.ctx.TexParameteri(texture2D, textureMinFilter, textureFilterToGL[textureFilter])
  142. p.ctx.TexParameteri(texture2D, textureMagFilter, textureFilterToGL[textureFilter])
  143. p.ctx.TexParameteri(texture2D, textureWrapS, clampToEdge)
  144. p.ctx.TexParameteri(texture2D, textureWrapT, clampToEdge)
  145. p.logError()
  146. return texture
  147. }
  148. func (p *painter) textureScale(v float32) float32 {
  149. if p.pixScale == 1.0 {
  150. return float32(math.Round(float64(v)))
  151. }
  152. return float32(math.Round(float64(v * p.pixScale)))
  153. }