github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/textinput/font.go (about)

     1  package main
     2  
     3  import (
     4  	"image"
     5  	"path/filepath"
     6  
     7  	"github.com/go-gl/gl/v2.1/gl"
     8  )
     9  
    10  type Point struct{ X, Y float32 }
    11  
    12  func (a Point) Add(b Point) Point {
    13  	return Point{
    14  		a.X + b.X,
    15  		a.Y + b.Y,
    16  	}
    17  }
    18  
    19  type GlyphRect struct {
    20  	Advance  float32
    21  	Min, Max Point
    22  }
    23  
    24  type Font struct {
    25  	Glyph map[rune]GlyphRect
    26  	RGBA  *image.RGBA
    27  	Width float32
    28  	GL    uint32
    29  }
    30  
    31  type FontInfo struct {
    32  	Path   string
    33  	Glyphs string
    34  	Size   Point
    35  	Widths map[float32]string
    36  }
    37  
    38  var ArcadeFont = FontInfo{
    39  	Path:   "arcade_43x74.png",
    40  	Glyphs: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.,;:?!-_~#\"'&()[]|`\\/@°+=*$£€<>%",
    41  	Size:   Point{43, 74},
    42  	Widths: map[float32]string{
    43  		12: "|",
    44  		15: "il;:'",
    45  		16: ".",
    46  		20: "[]",
    47  		21: ",`*",
    48  		25: "j()$<>",
    49  		27: "°",
    50  		30: "frt-\"&=",
    51  		34: "ITYkn01?/+%",
    52  		36: "_",
    53  		39: "ABCDEFGHJKLMNOPQRSUVWXZabcdeghmopqsuvwxyz23456789!#\\@£€",
    54  		41: "~",
    55  	},
    56  }
    57  
    58  func (info *FontInfo) Load() *Font {
    59  	m, err := loadImage(filepath.FromSlash(info.Path))
    60  	if err != nil {
    61  		panic(err)
    62  	}
    63  
    64  	widths := map[rune]float32{}
    65  	for size, runes := range info.Widths {
    66  		for _, r := range runes {
    67  			widths[r] = size
    68  		}
    69  	}
    70  
    71  	font := &Font{}
    72  	font.Glyph = make(map[rune]GlyphRect)
    73  	font.RGBA = m
    74  	font.Width = info.Size.X / info.Size.Y
    75  
    76  	sz := m.Bounds().Size()
    77  	u := Point{info.Size.X / float32(sz.X), info.Size.Y / float32(sz.Y)}
    78  	dot := Point{0, 0}
    79  	for _, r := range info.Glyphs {
    80  		advance := widths[r] / float32(info.Size.Y)
    81  		if advance == 0 {
    82  			advance = u.X / u.Y
    83  		}
    84  		font.Glyph[r] = GlyphRect{
    85  			Advance: advance,
    86  			Min:     dot,
    87  			Max:     dot.Add(u),
    88  		}
    89  
    90  		dot.X += u.X
    91  		if dot.X+u.X >= 1 {
    92  			dot.X = 0
    93  			dot.Y += u.Y
    94  		}
    95  	}
    96  
    97  	font.upload()
    98  	return font
    99  }
   100  
   101  func (font *Font) upload() {
   102  	gl.Enable(gl.TEXTURE_2D)
   103  	gl.GenTextures(1, &font.GL)
   104  	gl.BindTexture(gl.TEXTURE_2D, font.GL)
   105  
   106  	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
   107  	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
   108  
   109  	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
   110  	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
   111  	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE)
   112  
   113  	gl.TexImage2D(
   114  		gl.TEXTURE_2D,
   115  		0,
   116  		gl.RGBA,
   117  		int32(font.RGBA.Rect.Size().X),
   118  		int32(font.RGBA.Rect.Size().Y),
   119  		0,
   120  		gl.RGBA,
   121  		gl.UNSIGNED_BYTE,
   122  		gl.Ptr(font.RGBA.Pix))
   123  
   124  	gl.Disable(gl.TEXTURE_2D)
   125  }
   126  
   127  func (font *Font) DrawText(p Point, textHeight float32, text string) {
   128  	gl.ActiveTexture(gl.TEXTURE0)
   129  	gl.Enable(gl.TEXTURE_2D)
   130  	gl.BindTexture(gl.TEXTURE_2D, font.GL)
   131  	{
   132  		gl.Color4ub(0xff, 0xff, 0xff, 0xff)
   133  		gl.Begin(gl.QUADS)
   134  
   135  		dot := p
   136  		glyphWidth := font.Width * textHeight
   137  		for _, r := range text {
   138  			if r == '\n' {
   139  				dot.X = p.X
   140  				dot.Y += textHeight
   141  				continue
   142  			}
   143  
   144  			g, ok := font.Glyph[r]
   145  			if !ok {
   146  				dot.X += glyphWidth
   147  			}
   148  
   149  			gl.TexCoord2f(g.Min.X, g.Min.Y)
   150  			gl.Vertex2f(dot.X, dot.Y)
   151  
   152  			gl.TexCoord2f(g.Max.X, g.Min.Y)
   153  			gl.Vertex2f(dot.X+glyphWidth, dot.Y)
   154  
   155  			gl.TexCoord2f(g.Max.X, g.Max.Y)
   156  			gl.Vertex2f(dot.X+glyphWidth, dot.Y+textHeight)
   157  
   158  			gl.TexCoord2f(g.Min.X, g.Max.Y)
   159  			gl.Vertex2f(dot.X, dot.Y+textHeight)
   160  
   161  			dot.X += textHeight * g.Advance
   162  		}
   163  		gl.End()
   164  	}
   165  	gl.Disable(gl.TEXTURE_2D)
   166  }