Home

Awesome

etxt

Go Reference

NOTICE: this is a preview of v0.0.9, which is a non-trivial departure from previous versions. For the latest stable version, see v0.0.8.

etxt is a package for vectorial1 text rendering in Golang designed to be used with Ebitengine, the 2D game engine made by Hajime Hoshi.

While Ebitengine already includes the ebiten/v2/text/v2 package, etxt has some advantages over it:

What etxt doesn't do:

If you are unfamiliar with typography terms and concepts, I highly recommend reading the first chapters of FreeType Glyph Conventions; one the best references on the topic you can find on the internet.

Code example

Less talk and more code!

package main

import ( "math" ; "image/color" )
import "github.com/hajimehoshi/ebiten/v2"
import "github.com/tinne26/etxt"
import "github.com/tinne26/fonts/liberation/lbrtserif"

const WordsPerSec = 2.71828
var Words = []string {
	"solitude", "joy", "ride", "whisper", "leaves", "cookie",
	"hearts", "disdain", "simple", "death", "sea", "shallow",
	"self", "rhyme", "childish", "sky", "tic", "tac", "boom",
}

// ---- Ebitengine's Game interface implementation ----

type Game struct { text *etxt.Renderer ; wordIndex float64 }

func (self *Game) Layout(winWidth int, winHeight int) (int, int) {
	scale := ebiten.DeviceScaleFactor()
	self.text.SetScale(scale) // relevant for HiDPI
	canvasWidth  := int(math.Ceil(float64(winWidth)*scale))
	canvasHeight := int(math.Ceil(float64(winHeight)*scale))
	return canvasWidth, canvasHeight
}

func (self *Game) Update() error {
	newIndex := (self.wordIndex + WordsPerSec/60.0)
	self.wordIndex = math.Mod(newIndex, float64(len(Words)))
	return nil
}

func (self *Game) Draw(canvas *ebiten.Image) {
	// background color
	canvas.Fill(color.RGBA{229, 255, 222, 255})
	
	// get screen center position
	bounds := canvas.Bounds() // assumes origin (0, 0)
	x, y := bounds.Dx()/2, bounds.Dy()/2

	// draw text
	word := Words[int(self.wordIndex)]
	self.text.Draw(canvas, word, x, y)
}

// ---- main function ----

func main() {
	// create text renderer, set the font and cache
	renderer := etxt.NewRenderer()
	renderer.SetFont(lbrtserif.Font())
	renderer.Utils().SetCache8MiB()
	
	// adjust main text style properties
	renderer.SetColor(color.RGBA{239, 91, 91, 255})
	renderer.SetAlign(etxt.Center)
	renderer.SetSize(72)

	// set up Ebitengine and start the game
	ebiten.SetWindowTitle("etxt/examples/ebiten/words")
	err := ebiten.RunGame(&Game{ text: renderer })
	if err != nil { panic(err) }
}

You can try running this yourself with2:

go run github.com/tinne26/etxt/examples/ebiten/words@latest

Alternatively, you can go to https://tinne26.github.io/etxt-examples/ and click on the first example for the web version.

This is a very simple and self-contained example. If you want to learn more, make sure to take a look at etxt/examples!

Can I use this package without Ebitengine?

Yeah, you can compile it with -tags gtxt. Notice that gtxt will make text drawing happen on the CPU, so don't try to use it for real-time applications. In particular, be careful to not accidentally use gtxt with Ebitengine (they are compatible in many cases, but performance will die).

Testing, contributions and others

Footnotes

  1. If you are using pixel-art-like vectorial fonts, read these tips.

  2. You will need Golang >=1.18, and if you have never used Ebitengine before, you may need to install some dependencies (typically only on Linux or FreeBSD).