mirror of https://github.com/liamg/aminal.git
240 lines
5.8 KiB
Go
240 lines
5.8 KiB
Go
package glfont
|
|
|
|
import (
|
|
"os"
|
|
|
|
"github.com/go-gl/gl/all-core/gl"
|
|
)
|
|
|
|
// Direction represents the direction in which strings should be rendered.
|
|
type Direction uint8
|
|
|
|
// Known directions.
|
|
const (
|
|
LeftToRight Direction = iota // E.g.: Latin
|
|
RightToLeft // E.g.: Arabic
|
|
TopToBottom // E.g.: Chinese
|
|
)
|
|
|
|
// A Font allows rendering of text to an OpenGL context.
|
|
type Font struct {
|
|
fontChar []*character
|
|
vao uint32
|
|
vbo uint32
|
|
program uint32
|
|
texture uint32 // Holds the glyph texture id.
|
|
color color
|
|
}
|
|
|
|
type color struct {
|
|
r float32
|
|
g float32
|
|
b float32
|
|
a float32
|
|
}
|
|
|
|
//LoadFont loads the specified font at the given scale.
|
|
func LoadFont(file string, scale int32, windowWidth int, windowHeight int) (*Font, error) {
|
|
fd, err := os.Open(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer fd.Close()
|
|
|
|
// Configure the default font vertex and fragment shaders
|
|
program, err := newProgram(vertexFontShader, fragmentFontShader)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Activate corresponding render state
|
|
gl.UseProgram(program)
|
|
|
|
//set screen resolution
|
|
resUniform := gl.GetUniformLocation(program, gl.Str("resolution\x00"))
|
|
gl.Uniform2f(resUniform, float32(windowWidth), float32(windowHeight))
|
|
|
|
return LoadTrueTypeFont(program, fd, scale, 32, 127, LeftToRight)
|
|
}
|
|
|
|
//SetColor allows you to set the text color to be used when you draw the text
|
|
func (f *Font) SetColor(red float32, green float32, blue float32, alpha float32) {
|
|
f.color.r = red
|
|
f.color.g = green
|
|
f.color.b = blue
|
|
f.color.a = alpha
|
|
}
|
|
|
|
func (f *Font) UpdateResolution(windowWidth int, windowHeight int) {
|
|
gl.UseProgram(f.program)
|
|
resUniform := gl.GetUniformLocation(f.program, gl.Str("resolution\x00"))
|
|
gl.Uniform2f(resUniform, float32(windowWidth), float32(windowHeight))
|
|
gl.UseProgram(0)
|
|
}
|
|
|
|
//Printf draws a string to the screen, takes a list of arguments like printf
|
|
func (f *Font) Print(x, y float32, scale float32, text string) error {
|
|
|
|
indices := []rune(text)
|
|
|
|
if len(indices) == 0 {
|
|
return nil
|
|
}
|
|
|
|
lowChar := rune(32)
|
|
|
|
//setup blending mode
|
|
gl.Enable(gl.BLEND)
|
|
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
|
|
|
|
// Activate corresponding render state
|
|
gl.UseProgram(f.program)
|
|
//set text color
|
|
gl.Uniform4f(gl.GetUniformLocation(f.program, gl.Str("textColor\x00")), f.color.r, f.color.g, f.color.b, f.color.a)
|
|
//set screen resolution
|
|
//resUniform := gl.GetUniformLocation(f.program, gl.Str("resolution\x00"))
|
|
//gl.Uniform2f(resUniform, float32(2560), float32(1440))
|
|
|
|
gl.ActiveTexture(gl.TEXTURE0)
|
|
gl.BindVertexArray(f.vao)
|
|
|
|
// Iterate through all characters in string
|
|
for i := range indices {
|
|
|
|
//get rune
|
|
runeIndex := indices[i]
|
|
|
|
//skip runes that are not in font chacter range
|
|
if int(runeIndex)-int(lowChar) > len(f.fontChar) || runeIndex < lowChar {
|
|
continue
|
|
}
|
|
|
|
//find rune in fontChar list
|
|
ch := f.fontChar[runeIndex-lowChar]
|
|
|
|
//calculate position and size for current rune
|
|
xpos := x + float32(ch.bearingH)*scale
|
|
ypos := y - float32(ch.height-ch.bearingV)*scale
|
|
w := float32(ch.width) * scale
|
|
h := float32(ch.height) * scale
|
|
|
|
//set quad positions
|
|
var x1 = xpos
|
|
var x2 = xpos + w
|
|
var y1 = ypos
|
|
var y2 = ypos + h
|
|
|
|
//setup quad array
|
|
var vertices = []float32{
|
|
// X, Y, Z, U, V
|
|
// Front
|
|
x1, y1, 0.0, 0.0,
|
|
x2, y1, 1.0, 0.0,
|
|
x1, y2, 0.0, 1.0,
|
|
x1, y2, 0.0, 1.0,
|
|
x2, y1, 1.0, 0.0,
|
|
x2, y2, 1.0, 1.0}
|
|
|
|
// Render glyph texture over quad
|
|
gl.BindTexture(gl.TEXTURE_2D, ch.textureID)
|
|
// Update content of VBO memory
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, f.vbo)
|
|
|
|
//BufferSubData(target Enum, offset int, data []byte)
|
|
gl.BufferSubData(gl.ARRAY_BUFFER, 0, len(vertices)*4, gl.Ptr(vertices)) // Be sure to use glBufferSubData and not glBufferData
|
|
// Render quad
|
|
gl.DrawArrays(gl.TRIANGLES, 0, 24)
|
|
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
|
|
// Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
|
|
x += float32((ch.advance >> 6)) * scale // Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels))
|
|
|
|
}
|
|
|
|
//clear opengl textures and programs
|
|
gl.BindVertexArray(0)
|
|
gl.BindTexture(gl.TEXTURE_2D, 0)
|
|
gl.UseProgram(0)
|
|
gl.Disable(gl.BLEND)
|
|
|
|
return nil
|
|
}
|
|
|
|
//Width returns the width of a piece of text in pixels
|
|
func (f *Font) Width(scale float32, text string) float32 {
|
|
|
|
var width float32
|
|
|
|
indices := []rune(text)
|
|
|
|
if len(indices) == 0 {
|
|
return 0
|
|
}
|
|
|
|
lowChar := rune(32)
|
|
|
|
// Iterate through all characters in string
|
|
for i := range indices {
|
|
|
|
//get rune
|
|
runeIndex := indices[i]
|
|
|
|
//skip runes that are not in font chacter range
|
|
if int(runeIndex)-int(lowChar) > len(f.fontChar) || runeIndex < lowChar {
|
|
continue
|
|
}
|
|
|
|
//find rune in fontChar list
|
|
ch := f.fontChar[runeIndex-lowChar]
|
|
|
|
// Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
|
|
width += float32((ch.advance >> 6)) * scale // Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels))
|
|
|
|
}
|
|
|
|
return width
|
|
}
|
|
|
|
//Height returns the height of a piece of text in pixels
|
|
func (f *Font) Height(scale float32, text string) float32 {
|
|
|
|
var baseHeight float32
|
|
var height float32
|
|
|
|
indices := []rune(text)
|
|
|
|
if len(indices) == 0 {
|
|
return 0
|
|
}
|
|
|
|
lowChar := rune(32)
|
|
|
|
// Iterate through all characters in string
|
|
for i := range indices {
|
|
|
|
//get rune
|
|
runeIndex := indices[i]
|
|
|
|
if int(runeIndex) == 0x0a {
|
|
baseHeight = height
|
|
height = 0
|
|
}
|
|
|
|
//skip runes that are not in font chacter range
|
|
if int(runeIndex)-int(lowChar) > len(f.fontChar) || runeIndex < lowChar {
|
|
continue
|
|
}
|
|
|
|
//find rune in fontChar list
|
|
ch := f.fontChar[runeIndex-lowChar]
|
|
|
|
// Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
|
|
if float32(ch.height)*scale > height {
|
|
height = float32(ch.height) * scale
|
|
}
|
|
|
|
}
|
|
|
|
return baseHeight + height
|
|
}
|