Skip to content

Instantly share code, notes, and snippets.

@Zyko0
Created May 1, 2024 17:46
Show Gist options
  • Save Zyko0/413c536b625b7a7dc27ef2030ddfa027 to your computer and use it in GitHub Desktop.
Save Zyko0/413c536b625b7a7dc27ef2030ddfa027 to your computer and use it in GitHub Desktop.
text/v2: DrawTriangles implementation
package main
import (
"bytes"
"fmt"
"image/color"
"log"
"os"
"runtime/pprof"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/hajimehoshi/ebiten/v2/text/v2"
"golang.org/x/image/font/gofont/goregular"
)
const (
ScreenWidth, ScreenHeight = 1920, 1080
)
var (
glyphAtlas = ebiten.NewImage(256, 256)
source *text.GoTextFaceSource
face text.Face
)
func init() {
var err error
glyphAtlas.Fill(color.White)
source, err = text.NewGoTextFaceSource(bytes.NewReader(goregular.TTF))
if err != nil {
log.Fatalf("source goregular font: %v", err)
}
face = &text.GoTextFace{
Source: source,
Size: 14,
}
}
var (
boxIndices = [6]uint16{0, 1, 2, 1, 2, 3}
vertices []ebiten.Vertex
indices []uint16
glyphs []text.Glyph
)
func TextDraw(dst *ebiten.Image, str string, face text.Face, options *text.DrawOptions) {
var layoutOp text.LayoutOptions
var drawOp ebiten.DrawImageOptions
if options != nil {
layoutOp = options.LayoutOptions
drawOp = options.DrawImageOptions
}
geoM := drawOp.GeoM
vertices = vertices[:0]
indices = indices[:0]
var index int
x, y := geoM.Apply(0, 0)
for _, g := range text.AppendGlyphs(glyphs[:0], str, face, &layoutOp) {
if g.Image == nil {
continue
}
gx := x + g.X
gy := y + g.Y
rect := g.Image.Bounds()
vertices = append(vertices,
ebiten.Vertex{
DstX: float32(gx),
DstY: float32(gy),
SrcX: float32(rect.Min.X),
SrcY: float32(rect.Min.Y),
ColorR: 1,
ColorG: 1,
ColorB: 1,
ColorA: 1,
},
ebiten.Vertex{
DstX: float32(gx + float64(rect.Dx())),
DstY: float32(gy),
SrcX: float32(rect.Max.X),
SrcY: float32(rect.Min.Y),
ColorR: 1,
ColorG: 1,
ColorB: 1,
ColorA: 1,
},
ebiten.Vertex{
DstX: float32(gx),
DstY: float32(gy + float64(rect.Dy())),
SrcX: float32(rect.Min.X),
SrcY: float32(rect.Max.Y),
ColorR: 1,
ColorG: 1,
ColorB: 1,
ColorA: 1,
},
ebiten.Vertex{
DstX: float32(gx + float64(rect.Dx())),
DstY: float32(gy + float64(rect.Dy())),
SrcX: float32(rect.Max.X),
SrcY: float32(rect.Max.Y),
ColorR: 1,
ColorG: 1,
ColorB: 1,
ColorA: 1,
},
)
indiceCursor := uint16(index * 4)
indices = append(indices,
boxIndices[0]+indiceCursor,
boxIndices[1]+indiceCursor,
boxIndices[2]+indiceCursor,
boxIndices[3]+indiceCursor,
boxIndices[4]+indiceCursor,
boxIndices[5]+indiceCursor,
)
index++
}
dst.DrawTriangles(vertices, indices, glyphAtlas, nil)
}
type Game struct{}
func (g *Game) Update() error {
if ebiten.IsKeyPressed(ebiten.KeyEscape) {
return ebiten.Termination
}
return nil
}
const str = "My big sentence containing many glyphs resolving into probably too many DrawImage, but let's see, who am I to assess something without taking measurements first"
func (g *Game) Draw(screen *ebiten.Image) {
opts := &text.DrawOptions{}
opts.GeoM.Translate(64, 64)
text.Draw(screen, str, face, opts)
opts.GeoM.Translate(0, 64)
TextDraw(screen, str, face, opts)
ebitenutil.DebugPrint(screen,
fmt.Sprintf("TPS %.2f - FPS %.2f",
ebiten.ActualTPS(), ebiten.ActualFPS(),
),
)
}
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
return ScreenWidth, ScreenHeight
}
func main() {
ebiten.SetFullscreen(true)
ebiten.SetVsyncEnabled(false)
f, err := os.Create("profile.prof")
if err != nil {
log.Fatal(err)
}
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal(err)
}
defer pprof.StopCPUProfile()
if err := ebiten.RunGame(&Game{}); err != nil && err != ebiten.Termination {
log.Fatal(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment