| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- package giu
- import (
- "fmt"
- "log"
- "runtime"
- "strings"
- "sync"
- "github.com/AllenDang/go-findfont"
- "github.com/AllenDang/imgui-go"
- )
- var (
- shouldRebuildFontAtlas bool
- defaultFontSize float32 = 14
- stringMap sync.Map // key is rune, value indicates whether it's a new rune.
- defaultFonts []FontInfo
- extraFonts []FontInfo
- extraFontMap map[string]*imgui.Font
- )
- const (
- preRegisterString = " \"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
- windows = "windows"
- )
- // FontInfo represents a giu implementation of imgui font.
- type FontInfo struct {
- fontName string
- fontPath string
- fontByte []byte
- size float32
- }
- func (f *FontInfo) String() string {
- return fmt.Sprintf("%s:%.2f", f.fontName, f.size)
- }
- func (f *FontInfo) SetSize(size float32) *FontInfo {
- result := *f
- result.size = size
- for _, i := range extraFonts {
- if i.String() == result.String() {
- return &result
- }
- }
- extraFonts = append(extraFonts, result)
- shouldRebuildFontAtlas = true
- return &result
- }
- func initFontAtlasProcessor() {
- extraFontMap = make(map[string]*imgui.Font)
- // Pre register numbers
- tStr(preRegisterString)
- // Pre-register fonts
- os := runtime.GOOS
- switch os {
- case "darwin":
- // English font
- registerDefaultFont("Menlo", defaultFontSize)
- // Chinese font
- registerDefaultFont("STHeiti", defaultFontSize-1)
- // Jananese font
- registerDefaultFont("ヒラギノ角ゴシック W0", defaultFontSize+3)
- // Korean font
- registerDefaultFont("AppleSDGothicNeo", defaultFontSize+2)
- case windows:
- // English font
- registerDefaultFont("Calibri", defaultFontSize+2)
- // Chinese font
- registerDefaultFont("MSYH", defaultFontSize+2)
- // Japanese font
- registerDefaultFont("MSGOTHIC", defaultFontSize+2)
- // Korean font
- registerDefaultFont("MALGUNSL", defaultFontSize+2)
- case "linux":
- // English fonts
- registerDefaultFonts([]FontInfo{
- {
- fontName: "FreeSans.ttf",
- size: defaultFontSize + 1,
- },
- {
- fontName: "FiraCode-Medium",
- size: defaultFontSize + 1,
- },
- {
- fontName: "sans",
- size: defaultFontSize + 1,
- },
- })
- }
- }
- // SetDefaultFontSize sets the default font size. Invoke this before MasterWindow.NewMasterWindow(..).
- func SetDefaultFontSize(size float32) {
- defaultFontSize = size
- }
- // SetDefaultFont changes default font.
- func SetDefaultFont(fontName string, size float32) {
- fontPath, err := findfont.Find(fontName)
- if err != nil {
- log.Fatalf("Cannot find font %s", fontName)
- return
- }
- fontInfo := FontInfo{fontName: fontName, fontPath: fontPath, size: size}
- defaultFonts = append([]FontInfo{fontInfo}, defaultFonts...)
- }
- // SetDefaultFontFromBytes changes default font by bytes of the font file.
- func SetDefaultFontFromBytes(fontBytes []byte, size float32) {
- defaultFonts = append([]FontInfo{
- {
- fontByte: fontBytes,
- size: size,
- },
- }, defaultFonts...)
- }
- func GetDefaultFonts() []FontInfo {
- return defaultFonts
- }
- // AddFont adds font by name, if the font is found, return *FontInfo, otherwise return nil.
- // To use added font, use giu.Style().SetFont(...).
- func AddFont(fontName string, size float32) *FontInfo {
- fontPath, err := findfont.Find(fontName)
- if err != nil {
- fmt.Printf("[Warning]Cannot find font %s at system, related text will not be rendered.\n", fontName)
- return nil
- }
- fi := FontInfo{
- fontName: fontName,
- fontPath: fontPath,
- size: size,
- }
- extraFonts = append(extraFonts, fi)
- return &fi
- }
- // AddFontFromBytes does similar to AddFont, but using data from memory.
- func AddFontFromBytes(fontName string, fontBytes []byte, size float32) *FontInfo {
- fi := FontInfo{
- fontName: fontName,
- fontByte: fontBytes,
- size: size,
- }
- extraFonts = append(extraFonts, fi)
- return &fi
- }
- func registerDefaultFont(fontName string, size float32) {
- fontPath, err := findfont.Find(fontName)
- if err != nil {
- return
- }
- fontInfo := FontInfo{fontName: fontName, fontPath: fontPath, size: size}
- defaultFonts = append(defaultFonts, fontInfo)
- }
- func registerDefaultFonts(fontInfos []FontInfo) {
- var firstFoundFont *FontInfo
- for _, fi := range fontInfos {
- fontPath, err := findfont.Find(fi.fontName)
- if err == nil {
- firstFoundFont = &FontInfo{fontName: fi.fontName, fontPath: fontPath, size: fi.size}
- break
- }
- }
- if firstFoundFont != nil {
- defaultFonts = append(defaultFonts, *firstFoundFont)
- }
- }
- // Register string to font atlas builder.
- // Note only register strings that will be displayed on the UI.
- func tStr(str string) string {
- for _, s := range str {
- if _, ok := stringMap.Load(s); !ok {
- stringMap.Store(s, false)
- shouldRebuildFontAtlas = true
- }
- }
- return str
- }
- // Register string pointer to font atlas builder.
- // Note only register strings that will be displayed on the UI.
- func tStrPtr(str *string) *string {
- tStr(*str)
- return str
- }
- func tStrSlice(str []string) []string {
- for _, s := range str {
- tStr(s)
- }
- return str
- }
- // Rebuild font atlas when necessary.
- func rebuildFontAtlas() {
- if !shouldRebuildFontAtlas {
- return
- }
- fonts := Context.IO().Fonts()
- fonts.Clear()
- var sb strings.Builder
- stringMap.Range(func(k, v any) bool {
- stringMap.Store(k, true)
- if ks, ok := k.(rune); ok {
- sb.WriteRune(ks)
- }
- return true
- })
- ranges := imgui.NewGlyphRanges()
- builder := imgui.NewFontGlyphRangesBuilder()
- // Because we pre-regestered numbers, so default string map's length should greater then 11.
- if sb.Len() > len(preRegisterString) {
- builder.AddText(sb.String())
- } else {
- builder.AddRanges(fonts.GlyphRangesDefault())
- }
- builder.BuildRanges(ranges)
- if len(defaultFonts) > 0 {
- fontConfig := imgui.NewFontConfig()
- fontConfig.SetOversampleH(2)
- fontConfig.SetOversampleV(2)
- fontConfig.SetRasterizerMultiply(1.5)
- for i, fontInfo := range defaultFonts {
- if i > 0 {
- fontConfig.SetMergeMode(true)
- }
- // Scale font size with DPI scale factor
- if runtime.GOOS == windows {
- fontInfo.size *= Context.GetPlatform().GetContentScale()
- }
- if len(fontInfo.fontByte) == 0 {
- fonts.AddFontFromFileTTFV(fontInfo.fontPath, fontInfo.size, fontConfig, ranges.Data())
- } else {
- fonts.AddFontFromMemoryTTFV(fontInfo.fontByte, fontInfo.size, fontConfig, ranges.Data())
- }
- }
- // Fall back if no font is added
- if fonts.GetFontCount() == 0 {
- fonts.AddFontDefault()
- }
- } else {
- fonts.AddFontDefault()
- }
- // Add extra fonts
- for _, fontInfo := range extraFonts {
- // Scale font size with DPI scale factor
- if runtime.GOOS == windows {
- fontInfo.size *= Context.GetPlatform().GetContentScale()
- }
- // Store imgui.Font for PushFont
- var f imgui.Font
- if len(fontInfo.fontByte) == 0 {
- f = fonts.AddFontFromFileTTFV(fontInfo.fontPath, fontInfo.size, imgui.DefaultFontConfig, ranges.Data())
- } else {
- f = fonts.AddFontFromMemoryTTFV(fontInfo.fontByte, fontInfo.size, imgui.DefaultFontConfig, ranges.Data())
- }
- extraFontMap[fontInfo.String()] = &f
- }
- fontTextureImg := fonts.TextureDataRGBA32()
- Context.renderer.SetFontTexture(fontTextureImg)
- shouldRebuildFontAtlas = false
- }
|