mirror of
https://github.com/libretro/ludo.git
synced 2026-11-08 17:25:31 -10:00
Compare commits
17
Commits
master
...
kivutar/utf8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d535c9034d | ||
|
|
9a5746ad6f | ||
|
|
2c9b3785ef | ||
|
|
46274166ef | ||
|
|
dffa1cca74 | ||
|
|
aa687155bc | ||
|
|
b0281fe2ba | ||
|
|
8c81a1f89b | ||
|
|
55cc8436d7 | ||
|
|
c3e1f90e33 | ||
|
|
b82d4c1acb | ||
|
|
988afe9bbb | ||
|
|
f1d2ed0896 | ||
|
|
5352ab2bca | ||
|
|
ea3873befa | ||
|
|
65c7f62eb0 | ||
|
|
9cc51e50ff |
No files matched your search
+4
-4
@@ -9,16 +9,16 @@ func stackHintLeft(stack *float32, icon uint32, label string, h int) {
|
||||
menu.Font.SetColor(hintTextColor)
|
||||
menu.DrawImage(icon, *stack, float32(h)-79*menu.ratio, 70*menu.ratio, 70*menu.ratio, 1.0, 0, hintTextColor)
|
||||
*stack += 70 * menu.ratio
|
||||
menu.Font.Print(*stack, float32(h)-30*menu.ratio, 0.5*menu.ratio, label)
|
||||
*stack += menu.Font.Width(0.5*menu.ratio, label)
|
||||
menu.Font.Print(*stack, float32(h)-30*menu.ratio, menu.ratio, label)
|
||||
*stack += menu.Font.Width(menu.ratio, label)
|
||||
*stack += 32 * menu.ratio
|
||||
}
|
||||
|
||||
// Used to easily compose different hint bars based on the context.
|
||||
func stackHintRight(stack *float32, icon uint32, label string, h int) {
|
||||
*stack -= menu.Font.Width(0.5*menu.ratio, label)
|
||||
*stack -= menu.Font.Width(menu.ratio, label)
|
||||
menu.Font.SetColor(hintTextColor)
|
||||
menu.Font.Print(*stack, float32(h)-30*menu.ratio, 0.5*menu.ratio, label)
|
||||
menu.Font.Print(*stack, float32(h)-30*menu.ratio, menu.ratio, label)
|
||||
*stack -= 70 * menu.ratio
|
||||
menu.DrawImage(icon, *stack, float32(h)-79*menu.ratio, 70*menu.ratio, 70*menu.ratio, 1.0, 0, hintTextColor)
|
||||
*stack -= 32 * menu.ratio
|
||||
|
||||
+1
-1
@@ -143,7 +143,7 @@ func genericInput(list *entry, dt float32) {
|
||||
|
||||
// indexed allows jumping directly to the next letter in playlists
|
||||
func indexed(list *entry, offset int) int {
|
||||
curr := list.children[list.ptr].label[0]
|
||||
curr := firstRune(list.children[list.ptr].label)
|
||||
for i, t := range list.indexes {
|
||||
if curr == t.Char {
|
||||
if i+offset < 0 {
|
||||
|
||||
@@ -20,7 +20,7 @@ func (m *Menu) RenderNotifications() {
|
||||
fading = 1
|
||||
}
|
||||
offset := fading*h - h
|
||||
lw := m.Font.Width(0.5*m.ratio, n.Message)
|
||||
lw := m.Font.Width(m.ratio, n.Message)
|
||||
fg := severityFgColor[n.Severity]
|
||||
bg := severityBgColor[n.Severity]
|
||||
m.DrawRect(
|
||||
@@ -35,7 +35,7 @@ func (m *Menu) RenderNotifications() {
|
||||
m.Font.Print(
|
||||
45*m.ratio,
|
||||
(stack+offset)*m.ratio,
|
||||
0.5*m.ratio,
|
||||
m.ratio,
|
||||
n.Message,
|
||||
)
|
||||
stack += h + offset
|
||||
|
||||
+12
-5
@@ -35,7 +35,7 @@ type entry struct {
|
||||
children []entry // children entries
|
||||
ptr int // index of the active child
|
||||
indexes []struct {
|
||||
Char byte
|
||||
Char rune
|
||||
Index int
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,13 @@ type Scene interface {
|
||||
Entry() *entry
|
||||
}
|
||||
|
||||
func firstRune(s string) rune {
|
||||
for _, r := range s {
|
||||
return r
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// genericSegueMount is the smooth transition of the menu entries first appearance
|
||||
func genericSegueMount(list *entry) {
|
||||
for i := range list.children {
|
||||
@@ -198,21 +205,21 @@ func genericRender(list *entry) {
|
||||
menu.Font.Print(
|
||||
(670+1)*menu.ratio,
|
||||
float32(h)*e.yp+fontOffset+1*menu.ratio,
|
||||
0.5*menu.ratio, e.label)
|
||||
menu.ratio, e.label)
|
||||
menu.Font.SetColor(textColor.Alpha(e.labelAlpha))
|
||||
menu.Font.Print(
|
||||
670*menu.ratio,
|
||||
float32(h)*e.yp+fontOffset,
|
||||
0.5*menu.ratio, e.label)
|
||||
menu.ratio, e.label)
|
||||
|
||||
if e.widget != nil {
|
||||
e.widget(&e)
|
||||
} else if e.stringValue != nil {
|
||||
lw := menu.Font.Width(0.5*menu.ratio, e.stringValue())
|
||||
lw := menu.Font.Width(menu.ratio, e.stringValue())
|
||||
menu.Font.Print(
|
||||
float32(w)-lw-128*menu.ratio,
|
||||
float32(h)*e.yp+fontOffset,
|
||||
0.5*menu.ratio, e.stringValue())
|
||||
menu.ratio, e.stringValue())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,11 +28,11 @@ func buildCoreOptions() Scene {
|
||||
for _, v := range core.Options.Vars {
|
||||
v := v
|
||||
list.children = append(list.children, entry{
|
||||
label: strings.Replace(v.Desc, "%", "%%", -1),
|
||||
label: strings.ReplaceAll(v.Desc, "%", "%%"),
|
||||
icon: "subsetting",
|
||||
stringValue: func() string {
|
||||
val := v.Choices[v.Choice]
|
||||
return strings.Replace(val, "%", "%%", -1)
|
||||
return "◀ " + strings.ReplaceAll(val, "%", "%%") + " ▶"
|
||||
},
|
||||
incr: func(direction int) {
|
||||
v.Choice += direction
|
||||
|
||||
+12
-12
@@ -70,16 +70,16 @@ func (s *sceneDialog) render() {
|
||||
white,
|
||||
)
|
||||
|
||||
menu.Font.SetColor(titleTextColor)
|
||||
lw1 := menu.Font.Width(0.7*menu.ratio, s.title)
|
||||
menu.Font.Print(fw/2-lw1/2, fh/2-120*menu.ratio+20*menu.ratio, 0.7*menu.ratio, s.title)
|
||||
menu.FontLg.SetColor(titleTextColor)
|
||||
lw1 := menu.FontLg.Width(menu.ratio, s.title)
|
||||
menu.FontLg.Print(fw/2-lw1/2, fh/2-120*menu.ratio+20*menu.ratio, menu.ratio, s.title)
|
||||
menu.Font.SetColor(dialogTextColor)
|
||||
lw2 := menu.Font.Width(0.5*menu.ratio, s.line1)
|
||||
menu.Font.Print(fw/2-lw2/2, fh/2-30*menu.ratio+20*menu.ratio, 0.5*menu.ratio, s.line1)
|
||||
lw3 := menu.Font.Width(0.5*menu.ratio, s.line2)
|
||||
menu.Font.Print(fw/2-lw3/2, fh/2+30*menu.ratio+20*menu.ratio, 0.5*menu.ratio, s.line2)
|
||||
lw2 := menu.Font.Width(menu.ratio, s.line1)
|
||||
menu.Font.Print(fw/2-lw2/2, fh/2-30*menu.ratio+20*menu.ratio, menu.ratio, s.line1)
|
||||
lw3 := menu.Font.Width(menu.ratio, s.line2)
|
||||
menu.Font.Print(fw/2-lw3/2, fh/2+30*menu.ratio+20*menu.ratio, menu.ratio, s.line2)
|
||||
|
||||
menu.Font.SetColor(dialogTextColor)
|
||||
menu.FontSm.SetColor(dialogTextColor)
|
||||
|
||||
var margin float32 = 15
|
||||
|
||||
@@ -90,10 +90,10 @@ func (s *sceneDialog) render() {
|
||||
fw/2-width/2*menu.ratio+margin*menu.ratio,
|
||||
fh/2+height/2*menu.ratio-70*menu.ratio-margin*menu.ratio,
|
||||
70*menu.ratio, 70*menu.ratio, 1.0, 0, dialogTextColor)
|
||||
menu.Font.Print(
|
||||
menu.FontSm.Print(
|
||||
fw/2-width/2*menu.ratio+margin*menu.ratio+70*menu.ratio,
|
||||
fh/2+height/2*menu.ratio-23*menu.ratio-margin*menu.ratio,
|
||||
0.4*menu.ratio,
|
||||
menu.ratio,
|
||||
"NO")
|
||||
|
||||
menu.DrawImage(
|
||||
@@ -101,10 +101,10 @@ func (s *sceneDialog) render() {
|
||||
fw/2+width/2*menu.ratio-150*menu.ratio-margin*menu.ratio,
|
||||
fh/2+height/2*menu.ratio-70*menu.ratio-margin*menu.ratio,
|
||||
70*menu.ratio, 70*menu.ratio, 1.0, 0, dialogTextColor)
|
||||
menu.Font.Print(
|
||||
menu.FontSm.Print(
|
||||
fw/2+width/2*menu.ratio-150*menu.ratio-margin*menu.ratio+70*menu.ratio,
|
||||
fh/2+height/2*menu.ratio-23*menu.ratio-margin*menu.ratio,
|
||||
0.4*menu.ratio,
|
||||
menu.ratio,
|
||||
"YES")
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ func explorerIcon(f os.FileInfo) string {
|
||||
|
||||
func appendNode(list *sceneExplorer, fullPath string, name string, f os.FileInfo, exts []string, cb func(string), dirAction *entry, prettifier Prettifier) {
|
||||
// Check whether or not we are to display hidden files.
|
||||
if name[:1] == "." && !settings.Current.ShowHiddenFiles {
|
||||
if strings.HasPrefix(name, ".") && !settings.Current.ShowHiddenFiles {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -203,13 +203,13 @@ func (s *sceneHistory) render() {
|
||||
menu.Font.Print(
|
||||
(840+1)*menu.ratio,
|
||||
float32(h)*e.yp+fontOffset-slOffset+1*menu.ratio,
|
||||
0.5*menu.ratio, e.label)
|
||||
menu.ratio, e.label)
|
||||
menu.Font.SetColor(textColor.Alpha(e.labelAlpha))
|
||||
menu.Font.Print(
|
||||
840*menu.ratio,
|
||||
float32(h)*e.yp+fontOffset-slOffset,
|
||||
0.5*menu.ratio, e.label)
|
||||
stack += float32(int(menu.Font.Width(0.5*menu.ratio, e.label)))
|
||||
menu.ratio, e.label)
|
||||
stack += float32(int(menu.Font.Width(menu.ratio, e.label)))
|
||||
stack += 10
|
||||
|
||||
for _, tag := range e.tags {
|
||||
@@ -225,11 +225,11 @@ func (s *sceneHistory) render() {
|
||||
}
|
||||
}
|
||||
|
||||
menu.Font.SetColor(mutedTextColor.Alpha(e.subLabelAlpha))
|
||||
menu.Font.Print(
|
||||
menu.FontSm.SetColor(mutedTextColor.Alpha(e.subLabelAlpha))
|
||||
menu.FontSm.Print(
|
||||
840*menu.ratio,
|
||||
float32(h)*e.yp+fontOffset+60*menu.ratio-slOffset,
|
||||
0.5*menu.ratio, e.subLabel)
|
||||
menu.ratio, e.subLabel)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,12 +57,12 @@ func buildPlaylist(path string) Scene {
|
||||
// Index first letters of entries to allow quick jump to the next or previous
|
||||
// letter
|
||||
func buildIndexes(list *entry) {
|
||||
var last byte
|
||||
var last rune
|
||||
for i := 0; i < len(list.children); i++ {
|
||||
char := list.children[i].label[0]
|
||||
char := firstRune(list.children[i].label)
|
||||
if char != last {
|
||||
list.indexes = append(list.indexes, struct {
|
||||
Char byte
|
||||
Char rune
|
||||
Index int
|
||||
}{char, i})
|
||||
last = char
|
||||
@@ -241,14 +241,14 @@ func (s *scenePlaylist) render() {
|
||||
menu.Font.Print(
|
||||
(840+1)*menu.ratio,
|
||||
float32(h)*e.yp+fontOffset+1*menu.ratio,
|
||||
0.5*menu.ratio, e.label)
|
||||
menu.ratio, e.label)
|
||||
menu.Font.SetColor(textColor.Alpha(e.labelAlpha))
|
||||
menu.Font.Print(
|
||||
840*menu.ratio,
|
||||
float32(h)*e.yp+fontOffset,
|
||||
0.5*menu.ratio, e.label)
|
||||
menu.ratio, e.label)
|
||||
|
||||
stack += float32(int(menu.Font.Width(0.5*menu.ratio, e.label)))
|
||||
stack += float32(int(menu.Font.Width(menu.ratio, e.label)))
|
||||
stack += 10
|
||||
|
||||
for _, tag := range e.tags {
|
||||
|
||||
@@ -157,13 +157,13 @@ func (s *sceneSavestates) render() {
|
||||
menu.Font.Print(
|
||||
(840+1)*menu.ratio,
|
||||
float32(h)*e.yp+fontOffset+1*menu.ratio,
|
||||
0.5*menu.ratio, e.label)
|
||||
menu.ratio, e.label)
|
||||
|
||||
menu.Font.SetColor(textColor.Alpha(e.labelAlpha))
|
||||
menu.Font.Print(
|
||||
840*menu.ratio,
|
||||
float32(h)*e.yp+fontOffset,
|
||||
0.5*menu.ratio, e.label)
|
||||
menu.ratio, e.label)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ func buildSettings() Scene {
|
||||
icon: "folder",
|
||||
value: f.Value,
|
||||
stringValue: func() string {
|
||||
return "[" + utils.FileName(f.Value().(string)) + "]"
|
||||
return "【" + utils.FileName(f.Value().(string)) + "】"
|
||||
},
|
||||
widget: widgets[f.Tag("widget")],
|
||||
callbackOK: func() {
|
||||
|
||||
+8
-8
@@ -298,16 +298,16 @@ func (tabs sceneTabs) render() {
|
||||
stackWidth += e.width*menu.ratio + e.margin*menu.ratio
|
||||
|
||||
if e.labelAlpha > 0 {
|
||||
lw := menu.Font.Width(0.5*menu.ratio, e.label)
|
||||
lw := menu.Font.Width(menu.ratio, e.label)
|
||||
menu.Font.SetColor(textShadowColor.Alpha(e.labelAlpha / 2))
|
||||
menu.Font.Print(x-lw/2+1*menu.ratio, float32(int(float32(h)/2+(250+1)*menu.ratio)), 0.5*menu.ratio, e.label)
|
||||
menu.Font.Print(x-lw/2+1*menu.ratio, float32(int(float32(h)/2+(250+1)*menu.ratio)), menu.ratio, e.label)
|
||||
menu.Font.SetColor(lablColor.Alpha(e.labelAlpha))
|
||||
menu.Font.Print(x-lw/2, float32(int(float32(h)/2+250*menu.ratio)), 0.5*menu.ratio, e.label)
|
||||
lw = menu.Font.Width(0.4*menu.ratio, e.subLabel)
|
||||
menu.Font.SetColor(textShadowColor.Alpha(e.labelAlpha / 2))
|
||||
menu.Font.Print(x-lw/2+1*menu.ratio, float32(int(float32(h)/2+(330+1)*menu.ratio)), 0.4*menu.ratio, e.subLabel)
|
||||
menu.Font.SetColor(lablColor.Alpha(e.labelAlpha))
|
||||
menu.Font.Print(x-lw/2, float32(int(float32(h)/2+330*menu.ratio)), 0.4*menu.ratio, e.subLabel)
|
||||
menu.Font.Print(x-lw/2, float32(int(float32(h)/2+250*menu.ratio)), menu.ratio, e.label)
|
||||
lw = menu.FontSm.Width(menu.ratio, e.subLabel)
|
||||
menu.FontSm.SetColor(textShadowColor.Alpha(e.labelAlpha / 2))
|
||||
menu.FontSm.Print(x-lw/2+1*menu.ratio, float32(int(float32(h)/2+(330+1)*menu.ratio)), menu.ratio, e.subLabel)
|
||||
menu.FontSm.SetColor(lablColor.Alpha(e.labelAlpha))
|
||||
menu.FontSm.Print(x-lw/2, float32(int(float32(h)/2+330*menu.ratio)), menu.ratio, e.subLabel)
|
||||
}
|
||||
|
||||
menu.DrawImage(menu.icons["hexagon"],
|
||||
|
||||
@@ -22,10 +22,10 @@ import (
|
||||
// Widget sets the graphical representation of the value.
|
||||
type Settings struct {
|
||||
VideoFullscreen bool `hide:"ludos" toml:"video_fullscreen" label:"Video Fullscreen" fmt:"%t" widget:"switch"`
|
||||
VideoMonitorIndex int `toml:"video_monitor_index" label:"Video Monitor Index" fmt:"%d"`
|
||||
VideoFilter string `toml:"video_filter" label:"Video Filter" fmt:"<%s>"`
|
||||
VideoMonitorIndex int `toml:"video_monitor_index" label:"Video Monitor Index" fmt:"◀ %d ▶"`
|
||||
VideoFilter string `toml:"video_filter" label:"Video Filter" fmt:"◀ %s ▶"`
|
||||
VideoDarkMode bool `toml:"video_dark_mode" label:"Video Dark Mode" fmt:"%t" widget:"switch"`
|
||||
VideoTheme string `toml:"video_theme" label:"Video Theme" fmt:"<%s>"`
|
||||
VideoTheme string `toml:"video_theme" label:"Video Theme" fmt:"◀ %s ▶"`
|
||||
|
||||
AudioVolume float32 `toml:"audio_volume" label:"Audio Volume" fmt:"%.1f" widget:"range"`
|
||||
|
||||
|
||||
+325
-128
@@ -5,6 +5,7 @@ import (
|
||||
"image"
|
||||
"image/draw"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/go-gl/gl/v2.1/gl"
|
||||
@@ -14,7 +15,7 @@ import (
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
type character struct {
|
||||
type glyph struct {
|
||||
x, y int
|
||||
width int // Glyph width
|
||||
height int // Glyph height
|
||||
@@ -33,9 +34,11 @@ const (
|
||||
TopToBottom // E.g.: Chinese
|
||||
)
|
||||
|
||||
const fontRenderScale = 1
|
||||
|
||||
// A Font allows rendering of text to an OpenGL context.
|
||||
type Font struct {
|
||||
fontChar []*character
|
||||
glyphs map[rune]*glyph
|
||||
vao uint32
|
||||
vbo uint32
|
||||
program uint32
|
||||
@@ -43,19 +46,248 @@ type Font struct {
|
||||
color Color
|
||||
atlasWidth float32
|
||||
atlasHeight float32
|
||||
atlas *image.RGBA
|
||||
face font.Face
|
||||
ttf *truetype.Font
|
||||
scale int32
|
||||
lineHeight float32
|
||||
margin int
|
||||
nextX int
|
||||
nextY int
|
||||
}
|
||||
|
||||
type point [4]float32
|
||||
|
||||
func max(a, b float32) float32 {
|
||||
if a > b {
|
||||
return a
|
||||
type glyphMetrics struct {
|
||||
bounds fixed.Rectangle26_6
|
||||
advance fixed.Int26_6
|
||||
width int
|
||||
height int
|
||||
ascent int
|
||||
descent int
|
||||
}
|
||||
|
||||
func ceilFixed26_6(v fixed.Int26_6) int32 {
|
||||
return int32((v + 63) >> 6)
|
||||
}
|
||||
|
||||
func appendRange(runes []rune, start, end rune) []rune {
|
||||
for r := start; r <= end; r++ {
|
||||
runes = append(runes, r)
|
||||
}
|
||||
return b
|
||||
return runes
|
||||
}
|
||||
|
||||
func defaultRunes() []rune {
|
||||
runes := make([]rune, 0, 512)
|
||||
runes = appendRange(runes, 32, 126)
|
||||
runes = append(runes,
|
||||
'◀', '▶', '【', '】',
|
||||
'「', '」', '『', '』',
|
||||
'。', '、', '・', 'ー',
|
||||
'!', '?',
|
||||
'(', ')', '[', ']', '{', '}',
|
||||
)
|
||||
runes = appendRange(runes, 0xFF01, 0xFF0F) // fullwidth punctuation
|
||||
runes = appendRange(runes, 0xFF10, 0xFF19) // fullwidth digits
|
||||
runes = appendRange(runes, 0xFF1A, 0xFF20) // fullwidth : ; < = > ? @
|
||||
runes = appendRange(runes, 0xFF21, 0xFF3A) // fullwidth A-Z
|
||||
runes = appendRange(runes, 0xFF3B, 0xFF40) // fullwidth [ \ ] ^ _
|
||||
runes = appendRange(runes, 0xFF41, 0xFF5A) // fullwidth a-z
|
||||
runes = appendRange(runes, 0xFF5B, 0xFF65) // fullwidth { | } ~ and katakana punctuation
|
||||
runes = appendRange(runes, 0x00A0, 0x017F)
|
||||
runes = appendRange(runes, 0x3000, 0x303F)
|
||||
runes = appendRange(runes, 0x3040, 0x30FF)
|
||||
return runes
|
||||
}
|
||||
|
||||
func getGlyphMetrics(face font.Face, ttf *truetype.Font, ch rune, scale int32) (glyphMetrics, error) {
|
||||
gBnd, gAdv, ok := face.GlyphBounds(ch)
|
||||
if !ok {
|
||||
return glyphMetrics{}, fmt.Errorf("ttf face glyphBounds error")
|
||||
}
|
||||
|
||||
gh := ceilFixed26_6(gBnd.Max.Y - gBnd.Min.Y)
|
||||
gw := ceilFixed26_6(gBnd.Max.X - gBnd.Min.X)
|
||||
|
||||
// If gylph has no dimensions set to a max value
|
||||
if gw == 0 || gh == 0 {
|
||||
gBnd = ttf.Bounds(fixed.Int26_6(scale))
|
||||
gw = ceilFixed26_6(gBnd.Max.X - gBnd.Min.X)
|
||||
gh = ceilFixed26_6(gBnd.Max.Y - gBnd.Min.Y)
|
||||
|
||||
// Above can sometimes yield 0 for font smaller than 48pt, 1 is minimum
|
||||
if gw == 0 || gh == 0 {
|
||||
gw = 1
|
||||
gh = 1
|
||||
}
|
||||
}
|
||||
|
||||
// The glyph's ascent and descent equal -bounds.Min.Y and +bounds.Max.Y.
|
||||
gAscent := int(ceilFixed26_6(-gBnd.Min.Y))
|
||||
gdescent := int(ceilFixed26_6(gBnd.Max.Y))
|
||||
|
||||
return glyphMetrics{
|
||||
bounds: gBnd,
|
||||
advance: gAdv,
|
||||
width: int(gw),
|
||||
height: int(gh),
|
||||
ascent: gAscent,
|
||||
descent: gdescent,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func wrapGlyph(x, y *int, glyphWidth, lineHeight, atlasWidth, margin int) {
|
||||
if *x+glyphWidth+margin > atlasWidth {
|
||||
*x = margin
|
||||
*y += lineHeight + margin
|
||||
}
|
||||
}
|
||||
|
||||
func appendGlyph(face font.Face, ttf *truetype.Font, ch rune, x, y *int, lineHeight, atlasWidth float32, atlas *image.RGBA, fg *image.Uniform, scale int32, margin int) (*glyph, error) {
|
||||
char := new(glyph)
|
||||
|
||||
metrics, err := getGlyphMetrics(face, ttf, ch, scale)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wrapGlyph(x, y, metrics.width, int(lineHeight), int(atlasWidth), margin)
|
||||
if *y+metrics.height+margin > atlas.Rect.Dy() {
|
||||
return nil, fmt.Errorf("glyph atlas full")
|
||||
}
|
||||
|
||||
// Set w,h and adv, bearing V and bearing H in char
|
||||
char.x = *x
|
||||
char.y = *y
|
||||
char.width = metrics.width
|
||||
char.height = metrics.height
|
||||
char.advance = int(metrics.advance)
|
||||
char.bearingV = metrics.descent
|
||||
char.bearingH = (int(metrics.bounds.Min.X) >> 6)
|
||||
|
||||
clip := image.Rect(*x, *y, *x+metrics.width, *y+metrics.height)
|
||||
|
||||
// Create a freetype context for drawing
|
||||
c := freetype.NewContext()
|
||||
c.SetDPI(72)
|
||||
c.SetFont(ttf)
|
||||
c.SetFontSize(float64(scale))
|
||||
c.SetClip(clip)
|
||||
c.SetDst(atlas)
|
||||
c.SetSrc(fg)
|
||||
c.SetHinting(font.HintingFull)
|
||||
|
||||
// Set the glyph dot
|
||||
px := 0 - (int(metrics.bounds.Min.X) >> 6) + *x
|
||||
py := (metrics.ascent) + *y
|
||||
pt := freetype.Pt(px, py)
|
||||
|
||||
// Draw the text from mask to image
|
||||
_, err = c.DrawString(string(ch), pt)
|
||||
*x += metrics.width + margin
|
||||
return char, err
|
||||
}
|
||||
|
||||
func getMaxTextureSize() int32 {
|
||||
var maxTextureSize int32
|
||||
gl.GetIntegerv(gl.MAX_TEXTURE_SIZE, &maxTextureSize)
|
||||
if maxTextureSize == 0 {
|
||||
maxTextureSize = 4096
|
||||
}
|
||||
return maxTextureSize
|
||||
}
|
||||
|
||||
func calculateAtlasSize(face font.Face, ttf *truetype.Font, runes []rune, scale int32, margin int) (float32, float32, float32, error) {
|
||||
maxTextureSize := getMaxTextureSize()
|
||||
lineHeight := float32(face.Metrics().Height.Ceil())
|
||||
if lineHeight < 1 {
|
||||
lineHeight = 1
|
||||
}
|
||||
|
||||
width := 2048
|
||||
if width > int(maxTextureSize) {
|
||||
width = int(maxTextureSize)
|
||||
}
|
||||
|
||||
for {
|
||||
x := margin
|
||||
y := margin
|
||||
maxY := 0
|
||||
|
||||
for _, r := range runes {
|
||||
metrics, err := getGlyphMetrics(face, ttf, r, scale)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
wrapGlyph(&x, &y, metrics.width, int(lineHeight), width, margin)
|
||||
x += metrics.width + margin
|
||||
|
||||
if y+int(lineHeight) > maxY {
|
||||
maxY = y + int(lineHeight)
|
||||
}
|
||||
}
|
||||
|
||||
height := maxY + margin
|
||||
|
||||
if height <= int(maxTextureSize) || width >= int(maxTextureSize) {
|
||||
if height > int(maxTextureSize) {
|
||||
return 0, 0, 0, fmt.Errorf("glyph atlas %dx%d exceeds max texture size %d", width, height, maxTextureSize)
|
||||
}
|
||||
return float32(width), float32(height), lineHeight, nil
|
||||
}
|
||||
|
||||
width *= 2
|
||||
if width > int(maxTextureSize) {
|
||||
width = int(maxTextureSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func reserveAtlasHeight(baseHeight int, lineHeight float32, margin int, maxTextureSize int32) int {
|
||||
height := baseHeight * 2
|
||||
extraRowsHeight := baseHeight + 32*(int(lineHeight)+margin)
|
||||
if extraRowsHeight > height {
|
||||
height = extraRowsHeight
|
||||
}
|
||||
if height > int(maxTextureSize) {
|
||||
height = int(maxTextureSize)
|
||||
}
|
||||
return height
|
||||
}
|
||||
|
||||
func (f *Font) uploadGlyphRect(g *glyph) {
|
||||
if f.atlas == nil || g == nil {
|
||||
return
|
||||
}
|
||||
|
||||
offset := g.y*f.atlas.Stride + g.x*4
|
||||
if offset < 0 || offset >= len(f.atlas.Pix) {
|
||||
return
|
||||
}
|
||||
|
||||
gl.ActiveTexture(gl.TEXTURE0)
|
||||
gl.BindTexture(gl.TEXTURE_2D, f.textureID)
|
||||
gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1)
|
||||
gl.PixelStorei(gl.UNPACK_ROW_LENGTH, int32(f.atlas.Stride/4))
|
||||
gl.TexSubImage2D(
|
||||
gl.TEXTURE_2D,
|
||||
0,
|
||||
int32(g.x),
|
||||
int32(g.y),
|
||||
int32(g.width),
|
||||
int32(g.height),
|
||||
gl.RGBA,
|
||||
gl.UNSIGNED_BYTE,
|
||||
gl.Ptr(f.atlas.Pix[offset:]),
|
||||
)
|
||||
gl.PixelStorei(gl.UNPACK_ROW_LENGTH, 0)
|
||||
gl.BindTexture(gl.TEXTURE_2D, 0)
|
||||
}
|
||||
|
||||
// LoadTrueTypeFont builds a set of textures based on a ttf files gylphs
|
||||
func LoadTrueTypeFont(program uint32, r io.Reader, scale int32, low, high rune, dir Direction) (*Font, error) {
|
||||
func LoadTrueTypeFont(program uint32, r io.Reader, scale int32, dir Direction) (*Font, error) {
|
||||
data, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -69,120 +301,63 @@ func LoadTrueTypeFont(program uint32, r io.Reader, scale int32, low, high rune,
|
||||
|
||||
// Make Font stuct type
|
||||
f := new(Font)
|
||||
f.fontChar = make([]*character, 0, high-low+1)
|
||||
f.glyphs = make(map[rune]*glyph)
|
||||
f.program = program // Set shader program
|
||||
f.SetColor(Color{R: 1, G: 1, B: 1, A: 1}) // Set default white
|
||||
f.ttf = ttf
|
||||
f.scale = scale
|
||||
|
||||
// Create new face
|
||||
ttfFace := truetype.NewFace(ttf, &truetype.Options{
|
||||
face := truetype.NewFace(ttf, &truetype.Options{
|
||||
Size: float64(scale),
|
||||
DPI: 72,
|
||||
Hinting: font.HintingFull,
|
||||
})
|
||||
f.face = face
|
||||
|
||||
margin := 4
|
||||
runes := defaultRunes()
|
||||
var lineHeight float32
|
||||
f.atlasWidth = 1024
|
||||
f.atlasHeight = 1024
|
||||
for ch := low; ch <= high; ch++ {
|
||||
gBnd, _, ok := ttfFace.GlyphBounds(ch)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("ttf face glyphBounds error")
|
||||
}
|
||||
gh := int32((gBnd.Max.Y - gBnd.Min.Y) >> 6)
|
||||
lineHeight = max(lineHeight, float32(gh))
|
||||
|
||||
f.atlasWidth, f.atlasHeight, lineHeight, err = calculateAtlasSize(face, ttf, runes, scale, margin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.atlasHeight = float32(reserveAtlasHeight(int(f.atlasHeight), lineHeight, margin, getMaxTextureSize()))
|
||||
f.lineHeight = lineHeight
|
||||
f.margin = margin
|
||||
|
||||
// Create image to draw glyph
|
||||
fg, bg := image.White, image.Black
|
||||
rect := image.Rect(0, 0, int(f.atlasWidth), int(f.atlasHeight))
|
||||
rgba := image.NewRGBA(rect)
|
||||
draw.Draw(rgba, rgba.Bounds(), bg, image.Point{}, draw.Src)
|
||||
atlas := image.NewRGBA(rect)
|
||||
draw.Draw(atlas, atlas.Bounds(), bg, image.Point{}, draw.Src)
|
||||
f.atlas = atlas
|
||||
|
||||
margin := 4
|
||||
x := margin
|
||||
y := margin
|
||||
|
||||
// Make each gylph
|
||||
for ch := low; ch <= high; ch++ {
|
||||
char := new(character)
|
||||
|
||||
gBnd, gAdv, ok := ttfFace.GlyphBounds(ch)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("ttf face glyphBounds error")
|
||||
}
|
||||
|
||||
gh := int32((gBnd.Max.Y - gBnd.Min.Y) >> 6)
|
||||
gw := int32((gBnd.Max.X - gBnd.Min.X) >> 6)
|
||||
|
||||
// If gylph has no dimensions set to a max value
|
||||
if gw == 0 || gh == 0 {
|
||||
gBnd = ttf.Bounds(fixed.Int26_6(scale))
|
||||
gw = int32((gBnd.Max.X - gBnd.Min.X) >> 6)
|
||||
gh = int32((gBnd.Max.Y - gBnd.Min.Y) >> 6)
|
||||
|
||||
// Above can sometimes yield 0 for font smaller than 48pt, 1 is minimum
|
||||
if gw == 0 || gh == 0 {
|
||||
gw = 1
|
||||
gh = 1
|
||||
}
|
||||
}
|
||||
|
||||
// The glyph's ascent and descent equal -bounds.Min.Y and +bounds.Max.Y.
|
||||
gAscent := int(-gBnd.Min.Y) >> 6
|
||||
gdescent := int(gBnd.Max.Y) >> 6
|
||||
|
||||
// Set w,h and adv, bearing V and bearing H in char
|
||||
char.x = x
|
||||
char.y = y
|
||||
char.width = int(gw)
|
||||
char.height = int(gh)
|
||||
char.advance = int(gAdv)
|
||||
char.bearingV = gdescent
|
||||
char.bearingH = (int(gBnd.Min.X) >> 6)
|
||||
|
||||
clip := image.Rect(x, y, x+int(gw), y+int(gh))
|
||||
|
||||
// Create a freetype context for drawing
|
||||
c := freetype.NewContext()
|
||||
c.SetDPI(72)
|
||||
c.SetFont(ttf)
|
||||
c.SetFontSize(float64(scale))
|
||||
c.SetClip(clip)
|
||||
c.SetDst(rgba)
|
||||
c.SetSrc(fg)
|
||||
c.SetHinting(font.HintingFull)
|
||||
|
||||
// Set the glyph dot
|
||||
px := 0 - (int(gBnd.Min.X) >> 6) + x
|
||||
py := (gAscent) + y
|
||||
pt := freetype.Pt(px, py)
|
||||
|
||||
x += int(gw) + margin
|
||||
if x+int(gw)+margin > int(f.atlasWidth) {
|
||||
x = 0
|
||||
y += int(lineHeight) + margin
|
||||
}
|
||||
|
||||
// Draw the text from mask to image
|
||||
_, err = c.DrawString(string(ch), pt)
|
||||
for _, r := range runes {
|
||||
glyph, err := appendGlyph(face, ttf, r, &x, &y, lineHeight, f.atlasWidth, atlas, fg, scale, margin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
continue
|
||||
}
|
||||
|
||||
// Add char to fontChar list
|
||||
f.fontChar = append(f.fontChar, char)
|
||||
f.glyphs[r] = glyph
|
||||
}
|
||||
f.nextX = x
|
||||
f.nextY = y
|
||||
|
||||
// Generate texture
|
||||
gl.GenTextures(1, &f.textureID)
|
||||
gl.ActiveTexture(gl.TEXTURE0)
|
||||
gl.BindTexture(gl.TEXTURE_2D, f.textureID)
|
||||
gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
||||
|
||||
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(rgba.Rect.Dx()), int32(rgba.Rect.Dy()), 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(rgba.Pix))
|
||||
|
||||
gl.GenerateMipmap(gl.TEXTURE_2D)
|
||||
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(atlas.Rect.Dx()), int32(atlas.Rect.Dy()), 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(atlas.Pix))
|
||||
gl.BindTexture(gl.TEXTURE_2D, 0)
|
||||
|
||||
// Configure VAO/VBO for texture quads
|
||||
@@ -205,6 +380,25 @@ func LoadTrueTypeFont(program uint32, r io.Reader, scale int32, low, high rune,
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *Font) ensureGlyph(r rune) *glyph {
|
||||
if glyph, ok := f.glyphs[r]; ok {
|
||||
return glyph
|
||||
}
|
||||
if f.atlas == nil || f.face == nil || f.ttf == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
glyph, err := appendGlyph(f.face, f.ttf, r, &f.nextX, &f.nextY, f.lineHeight, f.atlasWidth, f.atlas, image.White, f.scale, f.margin)
|
||||
if err != nil {
|
||||
log.Printf("font: failed to append U+%04X %q: %v", r, string(r), err)
|
||||
return nil
|
||||
}
|
||||
f.glyphs[r] = glyph
|
||||
f.uploadGlyphRect(glyph)
|
||||
|
||||
return glyph
|
||||
}
|
||||
|
||||
// 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)
|
||||
@@ -226,7 +420,16 @@ func LoadFont(file string, scale int32, windowWidth int, windowHeight int) (*Fon
|
||||
resUniform := gl.GetUniformLocation(program, gl.Str("resolution\x00"))
|
||||
gl.Uniform2f(resUniform, float32(windowWidth), float32(windowHeight))
|
||||
|
||||
return LoadTrueTypeFont(program, fd, scale, 32, 256, LeftToRight)
|
||||
atlasScale := int32(float32(scale) / fontRenderScale)
|
||||
if atlasScale < 1 {
|
||||
atlasScale = 1
|
||||
}
|
||||
|
||||
font, err := LoadTrueTypeFont(program, fd, atlasScale, LeftToRight)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return font, nil
|
||||
}
|
||||
|
||||
// SetColor allows you to set the text color to be used when you draw the text
|
||||
@@ -245,13 +448,14 @@ func (f *Font) UpdateResolution(windowWidth int, windowHeight int) {
|
||||
// Print draws a string to the screen.
|
||||
func (f *Font) Print(x, y float32, scale float32, text string) error {
|
||||
indices := []rune(text)
|
||||
scale *= fontRenderScale
|
||||
x = float32(int(x + 0.5))
|
||||
y = float32(int(y + 0.5))
|
||||
|
||||
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)
|
||||
@@ -263,26 +467,23 @@ func (f *Font) Print(x, y float32, scale float32, text string) error {
|
||||
|
||||
var coords []point
|
||||
|
||||
// Iterate through all characters in string
|
||||
// Iterate through all glyphs in string
|
||||
for i := range indices {
|
||||
// Get rune
|
||||
runeIndex := indices[i]
|
||||
|
||||
var ch *character
|
||||
// Skip runes that are not in font chacter range
|
||||
if int(runeIndex)-int(lowChar) > len(f.fontChar) || runeIndex < lowChar {
|
||||
// Print a ?
|
||||
ch = f.fontChar[int(rune('?'))-int(lowChar)]
|
||||
} else {
|
||||
// Find rune in fontChar list
|
||||
ch = f.fontChar[runeIndex-lowChar]
|
||||
var ch *glyph
|
||||
// Skip runes that are not in font glyph map
|
||||
ch = f.ensureGlyph(runeIndex)
|
||||
if ch == nil {
|
||||
ch = f.glyphs['?'] // fallback
|
||||
}
|
||||
|
||||
// Calculate position and size for current rune
|
||||
xpos := x - 1 + float32(ch.bearingH)*scale
|
||||
ypos := y - 2 - float32(ch.height-ch.bearingV)*scale
|
||||
w := float32(ch.width+2) * scale
|
||||
h := float32(ch.height+2) * scale
|
||||
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
|
||||
@@ -290,12 +491,12 @@ func (f *Font) Print(x, y float32, scale float32, text string) error {
|
||||
var y1 = ypos
|
||||
var y2 = ypos + h
|
||||
|
||||
coords = append(coords, point{x1, y1, float32(ch.x-1) / f.atlasWidth, float32(ch.y-1) / f.atlasHeight})
|
||||
coords = append(coords, point{x2, y1, float32(ch.x+ch.width+1) / f.atlasWidth, float32(ch.y-1) / f.atlasHeight})
|
||||
coords = append(coords, point{x1, y2, float32(ch.x-1) / f.atlasWidth, float32(ch.y+ch.height+1) / f.atlasHeight})
|
||||
coords = append(coords, point{x2, y1, float32(ch.x+ch.width+1) / f.atlasWidth, float32(ch.y-1) / f.atlasHeight})
|
||||
coords = append(coords, point{x1, y2, float32(ch.x-1) / f.atlasWidth, float32(ch.y+ch.height+1) / f.atlasHeight})
|
||||
coords = append(coords, point{x2, y2, float32(ch.x+ch.width+1) / f.atlasWidth, float32(ch.y+ch.height+1) / f.atlasHeight})
|
||||
coords = append(coords, point{x1, y1, float32(ch.x) / f.atlasWidth, float32(ch.y) / f.atlasHeight})
|
||||
coords = append(coords, point{x2, y1, float32(ch.x+ch.width) / f.atlasWidth, float32(ch.y) / f.atlasHeight})
|
||||
coords = append(coords, point{x1, y2, float32(ch.x) / f.atlasWidth, float32(ch.y+ch.height) / f.atlasHeight})
|
||||
coords = append(coords, point{x2, y1, float32(ch.x+ch.width) / f.atlasWidth, float32(ch.y) / f.atlasHeight})
|
||||
coords = append(coords, point{x1, y2, float32(ch.x) / f.atlasWidth, float32(ch.y+ch.height) / f.atlasHeight})
|
||||
coords = append(coords, point{x2, y2, float32(ch.x+ch.width) / f.atlasWidth, float32(ch.y+ch.height) / f.atlasHeight})
|
||||
|
||||
// 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))
|
||||
@@ -323,6 +524,7 @@ func (f *Font) Printf(x, y float32, scale float32, format string, argv ...interf
|
||||
// Width returns the width of a piece of text in pixels.
|
||||
func (f *Font) Width(scale float32, text string) float32 {
|
||||
var width float32
|
||||
scale *= fontRenderScale
|
||||
|
||||
indices := []rune(text)
|
||||
|
||||
@@ -330,26 +532,21 @@ func (f *Font) Width(scale float32, text string) float32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
lowChar := rune(32)
|
||||
|
||||
// Iterate through all characters in string
|
||||
// Iterate through all glyphs 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 {
|
||||
fmt.Printf("%c %d\n", runeIndex, runeIndex)
|
||||
continue
|
||||
if f.face != nil {
|
||||
if advance, ok := f.face.GlyphAdvance(runeIndex); ok {
|
||||
width += float32(advance.Round()) * scale
|
||||
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))
|
||||
|
||||
if ch, ok := f.glyphs['?']; ok {
|
||||
width += float32((ch.advance >> 6)) * scale
|
||||
}
|
||||
}
|
||||
|
||||
return width
|
||||
|
||||
@@ -219,6 +219,7 @@ func textureLoad(nrgba *image.NRGBA) uint32 {
|
||||
gl.RGBA,
|
||||
gl.UNSIGNED_BYTE,
|
||||
gl.Ptr(nrgba.Pix))
|
||||
gl.ActiveTexture(gl.TEXTURE0)
|
||||
return texture
|
||||
}
|
||||
|
||||
|
||||
+21
-1
@@ -20,6 +20,8 @@ type Video struct {
|
||||
Window *glfw.Window
|
||||
Geom libretro.GameGeometry
|
||||
Font *Font
|
||||
FontSm *Font
|
||||
FontLg *Font
|
||||
|
||||
program uint32 // current program used for the game quad
|
||||
defaultProgram uint32 // default program used for the game quad
|
||||
@@ -128,7 +130,15 @@ func (video *Video) Configure(fullscreen bool) {
|
||||
|
||||
// LoadFont (fontfile, font scale, window width, window height)
|
||||
fontPath := filepath.Join(settings.Current.AssetsDirectory, "font.ttf")
|
||||
video.Font, err = LoadFont(fontPath, int32(36*2), fbw, fbh)
|
||||
video.Font, err = LoadFont(fontPath, int32(36), fbw, fbh)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
video.FontSm, err = LoadFont(fontPath, int32(29), fbw, fbh)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
video.FontLg, err = LoadFont(fontPath, int32(50), fbw, fbh)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -309,6 +319,15 @@ func (video *Video) coreRatioViewport(fbWidth int, fbHeight int) (x, y, w, h flo
|
||||
func (video *Video) ResizeViewport() {
|
||||
fbw, fbh := video.Window.GetFramebufferSize()
|
||||
gl.Viewport(0, 0, int32(fbw), int32(fbh))
|
||||
if video.Font != nil {
|
||||
video.Font.UpdateResolution(fbw, fbh)
|
||||
}
|
||||
if video.FontSm != nil {
|
||||
video.FontSm.UpdateResolution(fbw, fbh)
|
||||
}
|
||||
if video.FontLg != nil {
|
||||
video.FontLg.UpdateResolution(fbw, fbh)
|
||||
}
|
||||
}
|
||||
|
||||
// Render the current frame
|
||||
@@ -357,6 +376,7 @@ func (video *Video) uploadTexture() {
|
||||
return
|
||||
}
|
||||
|
||||
gl.ActiveTexture(gl.TEXTURE0)
|
||||
gl.BindTexture(gl.TEXTURE_2D, video.texID)
|
||||
gl.PixelStorei(gl.UNPACK_ROW_LENGTH, video.pitch/video.bpp)
|
||||
|
||||
|
||||
Reference in new issue
Block a user