Pad font atlas to avoid bleeding+togglable grid

This commit is contained in:
bloeys
2022-07-02 09:03:54 +04:00
parent ad759e03fc
commit 56a6e0868b
2 changed files with 35 additions and 18 deletions

View File

@ -70,13 +70,15 @@ func NewFontAtlasFromFont(f *truetype.Font, face font.Face, pointSize uint) (*Fo
atlasSizeX := 512 atlasSizeX := 512
atlasSizeY := 512 atlasSizeY := 512
charWidthFixed, _ := face.GlyphAdvance('L') const charPaddingX = 4
charWidth := charWidthFixed.Ceil() const charPaddingY = 4
charAdvFixed, _ := face.GlyphAdvance('L')
charAdv := charAdvFixed.Ceil() + charPaddingX
lineHeight := face.Metrics().Height.Ceil() lineHeight := face.Metrics().Height.Ceil()
maxLinesInAtlas := atlasSizeY/lineHeight - 1 maxLinesInAtlas := atlasSizeY/lineHeight - 1
charsPerLine := atlasSizeX / charWidth charsPerLine := atlasSizeX / charAdv
linesNeeded := int(math.Ceil(float64(len(glyphs)) / float64(charsPerLine))) linesNeeded := int(math.Ceil(float64(len(glyphs)) / float64(charsPerLine)))
for linesNeeded > maxLinesInAtlas { for linesNeeded > maxLinesInAtlas {
@ -86,7 +88,7 @@ func NewFontAtlasFromFont(f *truetype.Font, face font.Face, pointSize uint) (*Fo
maxLinesInAtlas = atlasSizeY/lineHeight - 1 maxLinesInAtlas = atlasSizeY/lineHeight - 1
charsPerLine = atlasSizeX / charWidth charsPerLine = atlasSizeX / charAdv
linesNeeded = int(math.Ceil(float64(len(glyphs)) / float64(charsPerLine))) linesNeeded = int(math.Ceil(float64(len(glyphs)) / float64(charsPerLine)))
} }
@ -114,6 +116,8 @@ func NewFontAtlasFromFont(f *truetype.Font, face font.Face, pointSize uint) (*Fo
//Put glyphs on atlas //Put glyphs on atlas
atlasSizeXF32 := float32(atlasSizeX) atlasSizeXF32 := float32(atlasSizeX)
atlasSizeYF32 := float32(atlasSizeY) atlasSizeYF32 := float32(atlasSizeY)
charPaddingXFixed := fixed.I(charPaddingX)
charPaddingYFixed := fixed.I(charPaddingY)
charsOnLine := 0 charsOnLine := 0
lineHeightFixed := fixed.I(lineHeight) lineHeightFixed := fixed.I(lineHeight)
@ -127,7 +131,7 @@ func NewFontAtlasFromFont(f *truetype.Font, face font.Face, pointSize uint) (*Fo
descent := absFixedI26_6(gBounds.Max.Y) descent := absFixedI26_6(gBounds.Max.Y)
bearingX := absFixedI26_6(gBounds.Min.X) bearingX := absFixedI26_6(gBounds.Min.X)
glyphWidth := float32((absFixedI26_6(gBounds.Max.X - gBounds.Min.X)).Ceil()) glyphWidth := float32((absFixedI26_6(gBounds.Max.X) - absFixedI26_6(gBounds.Min.X)).Ceil())
heightRounded := (ascent + descent).Ceil() heightRounded := (ascent + descent).Ceil()
atlas.Glyphs[g] = FontAtlasGlyph{ atlas.Glyphs[g] = FontAtlasGlyph{
@ -144,14 +148,16 @@ func NewFontAtlasFromFont(f *truetype.Font, face font.Face, pointSize uint) (*Fo
BearingX: float32(bearingX.Ceil()), BearingX: float32(bearingX.Ceil()),
Width: glyphWidth, Width: glyphWidth,
} }
drawer.DrawString(string(g)) drawer.DrawString(string(g))
drawer.Dot.X += charPaddingXFixed
charsOnLine++ charsOnLine++
if charsOnLine == charsPerLine { if charsOnLine == charsPerLine {
charsOnLine = 0 charsOnLine = 0
drawer.Dot.X = 0 drawer.Dot.X = 0
drawer.Dot.Y += lineHeightFixed drawer.Dot.Y += lineHeightFixed + charPaddingYFixed
} }
} }

35
main.go
View File

@ -27,6 +27,11 @@ type program struct {
FontSize uint32 FontSize uint32
Dpi float64 Dpi float64
GlyphRend *glyphs.GlyphRend GlyphRend *glyphs.GlyphRend
gridMesh *meshes.Mesh
gridMat *materials.Material
shouldDrawGrid bool
} }
//nMage TODO: //nMage TODO:
@ -61,7 +66,7 @@ func main() {
rend: rend, rend: rend,
imguiInfo: nmageimgui.NewImGUI(), imguiInfo: nmageimgui.NewImGUI(),
FontSize: 80, FontSize: 14,
} }
p.win.EventCallbacks = append(p.win.EventCallbacks, func(e sdl.Event) { p.win.EventCallbacks = append(p.win.EventCallbacks, func(e sdl.Event) {
@ -97,19 +102,16 @@ func (p *program) Init() {
glyphs.SaveImgToPNG(p.GlyphRend.Atlas.Img, "./debug-atlas.png") glyphs.SaveImgToPNG(p.GlyphRend.Atlas.Img, "./debug-atlas.png")
} }
var gridMesh *meshes.Mesh
var gridMat *materials.Material
func (p *program) Start() { func (p *program) Start() {
var err error var err error
gridMesh, err = meshes.NewMesh("grid", "./res/models/quad.obj", 0) p.gridMesh, err = meshes.NewMesh("grid", "./res/models/quad.obj", 0)
if err != nil { if err != nil {
panic(err.Error()) panic(err.Error())
} }
gridMat = materials.NewMaterial("grid", "./res/shaders/grid") p.gridMat = materials.NewMaterial("grid", "./res/shaders/grid")
gridMat.SetAttribute(gridMesh.Buf) p.gridMat.SetAttribute(p.gridMesh.Buf)
p.handleWindowResize() p.handleWindowResize()
} }
@ -126,6 +128,7 @@ func (p *program) Update() {
p.handleWindowResize() p.handleWindowResize()
} }
//Font sizing
oldFont := p.FontSize oldFont := p.FontSize
fontSizeChanged := false fontSizeChanged := false
if input.KeyClicked(sdl.K_KP_PLUS) { if input.KeyClicked(sdl.K_KP_PLUS) {
@ -144,10 +147,11 @@ func (p *program) Update() {
println("Failed to update font face. Err: " + err.Error()) println("Failed to update font face. Err: " + err.Error())
} else { } else {
glyphs.SaveImgToPNG(p.GlyphRend.Atlas.Img, "./debug-atlas.png") glyphs.SaveImgToPNG(p.GlyphRend.Atlas.Img, "./debug-atlas.png")
println("New font size:", p.FontSize) println("New font size:", p.FontSize, "; New texture size:", p.GlyphRend.Atlas.Img.Rect.Max.X, "\n")
} }
} }
//Move text
var speed float32 = 1 var speed float32 = 1
if input.KeyDown(sdl.K_RIGHT) { if input.KeyDown(sdl.K_RIGHT) {
xOff += speed xOff += speed
@ -160,6 +164,11 @@ func (p *program) Update() {
} else if input.KeyDown(sdl.K_DOWN) { } else if input.KeyDown(sdl.K_DOWN) {
yOff -= speed yOff -= speed
} }
//Grid
if input.KeyClicked(sdl.K_SPACE) {
p.shouldDrawGrid = !p.shouldDrawGrid
}
} }
var xOff float32 = 0 var xOff float32 = 0
@ -175,7 +184,9 @@ func (p *program) Render() {
// p.GlyphRend.DrawTextOpenGLAbs("Hello there, friend.\nABCDEFGHIJKLMNOPQRSTUVWXYZ", gglm.NewVec3(0, 0, 0), textColor) // p.GlyphRend.DrawTextOpenGLAbs("Hello there, friend.\nABCDEFGHIJKLMNOPQRSTUVWXYZ", gglm.NewVec3(0, 0, 0), textColor)
p.GlyphRend.DrawTextOpenGLAbs(" Hello there, friend|.\n ABCDEFGHIJKLMNOPQRSTUVWXYZ", gglm.NewVec3(xOff, float32(p.GlyphRend.Atlas.LineHeight)*2+yOff, 0), textColor) p.GlyphRend.DrawTextOpenGLAbs(" Hello there, friend|.\n ABCDEFGHIJKLMNOPQRSTUVWXYZ", gglm.NewVec3(xOff, float32(p.GlyphRend.Atlas.LineHeight)*2+yOff, 0), textColor)
// p.drawGrid() if p.shouldDrawGrid {
p.drawGrid()
}
} }
func (p *program) drawGrid() { func (p *program) drawGrid() {
@ -186,12 +197,12 @@ func (p *program) drawGrid() {
//columns //columns
adv := p.GlyphRend.Atlas.Glyphs['A'].Advance adv := p.GlyphRend.Atlas.Glyphs['A'].Advance
for i := int32(0); i < p.GlyphRend.ScreenWidth; i += int32(adv) { for i := int32(0); i < p.GlyphRend.ScreenWidth; i += int32(adv) {
p.rend.Draw(gridMesh, gglm.NewTrMatId().Translate(gglm.NewVec3(float32(i)+0.5, sizeY/2, 0)).Scale(gglm.NewVec3(1, sizeY, 1)), gridMat) p.rend.Draw(p.gridMesh, gglm.NewTrMatId().Translate(gglm.NewVec3(float32(i)+0.5, sizeY/2, 0)).Scale(gglm.NewVec3(1, sizeY, 1)), p.gridMat)
} }
//rows //rows
for i := int32(0); i < p.GlyphRend.ScreenHeight; i += int32(p.GlyphRend.Atlas.LineHeight) { for i := int32(0); i < p.GlyphRend.ScreenHeight; i += int32(p.GlyphRend.Atlas.LineHeight) {
p.rend.Draw(gridMesh, gglm.NewTrMatId().Translate(gglm.NewVec3(sizeX/2, float32(i), 0)).Scale(gglm.NewVec3(sizeX, 1, 1)), gridMat) p.rend.Draw(p.gridMesh, gglm.NewTrMatId().Translate(gglm.NewVec3(sizeX/2, float32(i), 0)).Scale(gglm.NewVec3(sizeX, 1, 1)), p.gridMat)
} }
} }
@ -220,5 +231,5 @@ func (p *program) handleWindowResize() {
projMtx := gglm.Ortho(0, float32(w), float32(h), 0, 0.1, 20) projMtx := gglm.Ortho(0, float32(w), float32(h), 0, 0.1, 20)
viewMtx := gglm.LookAt(gglm.NewVec3(0, 0, -10), gglm.NewVec3(0, 0, 0), gglm.NewVec3(0, 1, 0)) viewMtx := gglm.LookAt(gglm.NewVec3(0, 0, -10), gglm.NewVec3(0, 0, 0), gglm.NewVec3(0, 1, 0))
gridMat.SetUnifMat4("projViewMat", &projMtx.Mul(viewMtx).Mat4) p.gridMat.SetUnifMat4("projViewMat", &projMtx.Mul(viewMtx).Mat4)
} }