mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-02-07 00:56:51 -05:00
Fix CJK render problem partially (#222)
* Fixed CJK render problem * Add partial support of loading CJK fonts
This commit is contained in:
parent
23e228d88b
commit
30c3bb7330
@ -40,19 +40,19 @@ import (
|
|||||||
// Engine is the core OpenDiablo2 engine
|
// Engine is the core OpenDiablo2 engine
|
||||||
type Engine struct {
|
type Engine struct {
|
||||||
Settings *d2corecommon.Configuration // Engine configuration settings from json file
|
Settings *d2corecommon.Configuration // Engine configuration settings from json file
|
||||||
Files map[string]string // Map that defines which files are in which MPQs
|
Files map[string]string // Map that defines which files are in which MPQs
|
||||||
CheckedPatch map[string]bool // First time we check a file, we'll check if it's in the patch. This notes that we've already checked that.
|
CheckedPatch map[string]bool // First time we check a file, we'll check if it's in the patch. This notes that we've already checked that.
|
||||||
LoadingSprite d2render.Sprite // The sprite shown when loading stuff
|
LoadingSprite d2render.Sprite // The sprite shown when loading stuff
|
||||||
loadingProgress float64 // LoadingProcess is a range between 0.0 and 1.0. If set, loading screen displays.
|
loadingProgress float64 // LoadingProcess is a range between 0.0 and 1.0. If set, loading screen displays.
|
||||||
loadingIndex int // Determines which load function is currently being called
|
loadingIndex int // Determines which load function is currently being called
|
||||||
thingsToLoad []func() // The load functions for the next scene
|
thingsToLoad []func() // The load functions for the next scene
|
||||||
stepLoadingSize float64 // The size for each loading step
|
stepLoadingSize float64 // The size for each loading step
|
||||||
CurrentScene d2coreinterface.Scene // The current scene being rendered
|
CurrentScene d2coreinterface.Scene // The current scene being rendered
|
||||||
UIManager *d2ui.Manager // The UI manager
|
UIManager *d2ui.Manager // The UI manager
|
||||||
SoundManager *d2audio.Manager // The sound manager
|
SoundManager *d2audio.Manager // The sound manager
|
||||||
nextScene d2coreinterface.Scene // The next scene to be loaded at the end of the game loop
|
nextScene d2coreinterface.Scene // The next scene to be loaded at the end of the game loop
|
||||||
fullscreenKey bool // When true, the fullscreen toggle is still being pressed
|
fullscreenKey bool // When true, the fullscreen toggle is still being pressed
|
||||||
lastTime float64 // Last time we updated the scene
|
lastTime float64 // Last time we updated the scene
|
||||||
showFPS bool
|
showFPS bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,11 +104,18 @@ var mutex sync.Mutex
|
|||||||
|
|
||||||
func (v *Engine) LoadFile(fileName string) []byte {
|
func (v *Engine) LoadFile(fileName string) []byte {
|
||||||
fileName = strings.ReplaceAll(fileName, "{LANG}", d2resource.LanguageCode)
|
fileName = strings.ReplaceAll(fileName, "{LANG}", d2resource.LanguageCode)
|
||||||
|
// todo: separate CJK and latin characters from LanguageCode
|
||||||
|
if "CHI" == strings.ToUpper(d2resource.LanguageCode) {
|
||||||
|
fileName = strings.ReplaceAll(fileName, "{LANG_FONT}", d2resource.LanguageCode)
|
||||||
|
} else {
|
||||||
|
fileName = strings.ReplaceAll(fileName, "{LANG_FONT}", "latin")
|
||||||
|
}
|
||||||
fileName = strings.ToLower(fileName)
|
fileName = strings.ToLower(fileName)
|
||||||
fileName = strings.ReplaceAll(fileName, `/`, "\\")
|
fileName = strings.ReplaceAll(fileName, `/`, "\\")
|
||||||
if fileName[0] == '\\' {
|
if fileName[0] == '\\' {
|
||||||
fileName = fileName[1:]
|
fileName = fileName[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
// TODO: May want to cache some things if performance becomes an issue
|
// TODO: May want to cache some things if performance becomes an issue
|
||||||
|
@ -15,6 +15,10 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2render"
|
"github.com/OpenDiablo2/OpenDiablo2/d2render"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
|
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
var fontCache = map[string]*Font{}
|
var fontCache = map[string]*Font{}
|
||||||
@ -28,7 +32,8 @@ type FontSize struct {
|
|||||||
// Font represents a font
|
// Font represents a font
|
||||||
type Font struct {
|
type Font struct {
|
||||||
fontSprite d2render.Sprite
|
fontSprite d2render.Sprite
|
||||||
metrics map[uint8]FontSize
|
fontTable map[uint16]uint16
|
||||||
|
metrics map[uint16]FontSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFont creates or loads an existing font
|
// GetFont creates or loads an existing font
|
||||||
@ -45,21 +50,39 @@ func GetFont(font string, palette d2enum.PaletteType, fileProvider d2interface.F
|
|||||||
// CreateFont creates an instance of a MPQ Font
|
// CreateFont creates an instance of a MPQ Font
|
||||||
func CreateFont(font string, palette d2enum.PaletteType, fileProvider d2interface.FileProvider) *Font {
|
func CreateFont(font string, palette d2enum.PaletteType, fileProvider d2interface.FileProvider) *Font {
|
||||||
result := &Font{
|
result := &Font{
|
||||||
metrics: make(map[uint8]FontSize),
|
fontTable: make(map[uint16]uint16),
|
||||||
|
metrics: make(map[uint16]FontSize),
|
||||||
}
|
}
|
||||||
|
// bug: performance issue when using CJK fonts, because ten thousand frames will be rendered PER font
|
||||||
result.fontSprite = d2render.CreateSprite(fileProvider.LoadFile(font+".dc6"), d2datadict.Palettes[palette])
|
result.fontSprite = d2render.CreateSprite(fileProvider.LoadFile(font+".dc6"), d2datadict.Palettes[palette])
|
||||||
woo := "Woo!\x01"
|
woo := "Woo!\x01"
|
||||||
fontData := fileProvider.LoadFile(font + ".tbl")
|
fontData := fileProvider.LoadFile(font + ".tbl")
|
||||||
if string(fontData[0:5]) != woo {
|
if string(fontData[0:5]) != woo {
|
||||||
panic("No woo :(")
|
panic("No woo :(")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
containsCjk := false
|
||||||
for i := 12; i < len(fontData); i += 14 {
|
for i := 12; i < len(fontData); i += 14 {
|
||||||
fontSize := FontSize{
|
// font mappings, map unicode code points to array indics
|
||||||
Width: fontData[i+3],
|
unicodeCode := binary.LittleEndian.Uint16(fontData[i : i+2])
|
||||||
Height: fontData[i+4],
|
fontIndex := binary.LittleEndian.Uint16(fontData[i+8 : i+10])
|
||||||
|
result.fontTable[unicodeCode] = fontIndex
|
||||||
|
|
||||||
|
if unicodeCode < unicode.MaxLatin1 {
|
||||||
|
result.metrics[unicodeCode] = FontSize{
|
||||||
|
Width: fontData[i+3],
|
||||||
|
Height: fontData[i+4],
|
||||||
|
}
|
||||||
|
} else if !containsCjk {
|
||||||
|
// CJK characters are all in the same size
|
||||||
|
result.metrics[unicode.MaxLatin1] = FontSize{
|
||||||
|
Width: fontData[i+3],
|
||||||
|
Height: fontData[i+4],
|
||||||
|
}
|
||||||
|
containsCjk = true
|
||||||
}
|
}
|
||||||
result.metrics[fontData[i+8]] = fontSize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,19 +92,19 @@ func (v *Font) GetTextMetrics(text string) (width, height uint32) {
|
|||||||
curWidth := uint32(0)
|
curWidth := uint32(0)
|
||||||
height = uint32(0)
|
height = uint32(0)
|
||||||
maxCharHeight := uint32(0)
|
maxCharHeight := uint32(0)
|
||||||
|
// todo: it can be saved as a struct member, since it only depends on `.Frames`
|
||||||
for _, m := range v.fontSprite.Frames {
|
for _, m := range v.fontSprite.Frames {
|
||||||
maxCharHeight = d2helper.Max(maxCharHeight, uint32(m.Height))
|
maxCharHeight = d2helper.Max(maxCharHeight, uint32(m.Height))
|
||||||
}
|
}
|
||||||
for i := 0; i < len(text); i++ {
|
for _, ch := range text {
|
||||||
ch := text[i]
|
|
||||||
if ch == '\n' {
|
if ch == '\n' {
|
||||||
width = d2helper.Max(width, curWidth)
|
width = d2helper.Max(width, curWidth)
|
||||||
curWidth = 0
|
curWidth = 0
|
||||||
height += maxCharHeight + 6
|
height += maxCharHeight + 6
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
metric := v.metrics[uint8(ch)]
|
|
||||||
curWidth += uint32(metric.Width)
|
curWidth += v.getCharWidth(ch)
|
||||||
}
|
}
|
||||||
width = d2helper.Max(width, curWidth)
|
width = d2helper.Max(width, curWidth)
|
||||||
height += maxCharHeight
|
height += maxCharHeight
|
||||||
@ -105,12 +128,12 @@ func (v *Font) Draw(x, y int, text string, color color.Color, target *ebiten.Ima
|
|||||||
xPos := x + ((targetWidth / 2) - int(lineWidth/2))
|
xPos := x + ((targetWidth / 2) - int(lineWidth/2))
|
||||||
|
|
||||||
for _, ch := range line {
|
for _, ch := range line {
|
||||||
char := uint8(ch)
|
width := v.getCharWidth(ch)
|
||||||
metric := v.metrics[char]
|
index := v.fontTable[uint16(ch)]
|
||||||
v.fontSprite.Frame = int16(char)
|
v.fontSprite.Frame = int16(index)
|
||||||
v.fontSprite.MoveTo(xPos, y+int(v.fontSprite.Frames[char].Height))
|
v.fontSprite.MoveTo(xPos, y+int(v.fontSprite.Frames[index].Height))
|
||||||
v.fontSprite.Draw(target)
|
v.fontSprite.Draw(target)
|
||||||
xPos += int(metric.Width)
|
xPos += int(width)
|
||||||
}
|
}
|
||||||
|
|
||||||
if lineIdx >= len(lines)-1 {
|
if lineIdx >= len(lines)-1 {
|
||||||
@ -121,3 +144,10 @@ func (v *Font) Draw(x, y int, text string, color color.Color, target *ebiten.Ima
|
|||||||
y += int(maxCharHeight + 6)
|
y += int(maxCharHeight + 6)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Font) getCharWidth(char rune) (width uint32) {
|
||||||
|
if char < unicode.MaxLatin1 {
|
||||||
|
return uint32(v.metrics[uint16(char)].Width)
|
||||||
|
}
|
||||||
|
return uint32(v.metrics[unicode.MaxLatin1].Width)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user