mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-02-20 23:47:16 -05:00
Merge pull request #1053 from gucio321/date-encoder-font
Date encoder: font table
This commit is contained in:
commit
6a94dfcfcf
3
d2common/d2fileformats/d2font/doc.go
Normal file
3
d2common/d2fileformats/d2font/doc.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// Package d2font contains logic for loading and processing
|
||||||
|
// d2 fonts
|
||||||
|
package d2font
|
235
d2common/d2fileformats/d2font/font.go
Normal file
235
d2common/d2fileformats/d2font/font.go
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
package d2font
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image/color"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2datautils"
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
knownSignature = "Woo!\x01"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
signatureBytesCount = 5
|
||||||
|
unknownHeaderBytesCount = 7
|
||||||
|
unknown1BytesCount = 1
|
||||||
|
unknown2BytesCount = 3
|
||||||
|
unknown3BytesCount = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
type fontGlyph struct {
|
||||||
|
unknown1 []byte
|
||||||
|
unknown2 []byte
|
||||||
|
unknown3 []byte
|
||||||
|
frame int
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fg *fontGlyph) setHeight(h int) {
|
||||||
|
fg.height = h
|
||||||
|
}
|
||||||
|
|
||||||
|
// Font represents a displayable font
|
||||||
|
type Font struct {
|
||||||
|
unknownHeaderBytes []byte
|
||||||
|
sheet d2interface.Animation
|
||||||
|
table []byte
|
||||||
|
glyphs map[rune]*fontGlyph
|
||||||
|
color color.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load loads a new font from byte slice
|
||||||
|
func Load(data []byte) (*Font, error) {
|
||||||
|
sr := d2datautils.CreateStreamReader(data)
|
||||||
|
|
||||||
|
signature, err := sr.ReadBytes(signatureBytesCount)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(signature) != knownSignature {
|
||||||
|
return nil, fmt.Errorf("invalid font table format")
|
||||||
|
}
|
||||||
|
|
||||||
|
font := &Font{
|
||||||
|
table: data,
|
||||||
|
color: color.White,
|
||||||
|
}
|
||||||
|
|
||||||
|
font.unknownHeaderBytes, err = sr.ReadBytes(unknownHeaderBytesCount)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = font.initGlyphs(sr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return font, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBackground sets font's background
|
||||||
|
func (f *Font) SetBackground(sheet d2interface.Animation) {
|
||||||
|
f.sheet = sheet
|
||||||
|
|
||||||
|
// recalculate max height
|
||||||
|
_, h := f.sheet.GetFrameBounds()
|
||||||
|
|
||||||
|
for i := range f.glyphs {
|
||||||
|
f.glyphs[i].setHeight(h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetColor sets the fonts color
|
||||||
|
func (f *Font) SetColor(c color.Color) {
|
||||||
|
f.color = c
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTextMetrics returns the dimensions of the Font element in pixels
|
||||||
|
func (f *Font) GetTextMetrics(text string) (width, height int) {
|
||||||
|
var (
|
||||||
|
lineWidth int
|
||||||
|
lineHeight int
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, c := range text {
|
||||||
|
if c == '\n' {
|
||||||
|
width = d2math.MaxInt(width, lineWidth)
|
||||||
|
height += lineHeight
|
||||||
|
lineWidth = 0
|
||||||
|
lineHeight = 0
|
||||||
|
} else if glyph, ok := f.glyphs[c]; ok {
|
||||||
|
lineWidth += glyph.width
|
||||||
|
lineHeight = d2math.MaxInt(lineHeight, glyph.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
width = d2math.MaxInt(width, lineWidth)
|
||||||
|
height += lineHeight
|
||||||
|
|
||||||
|
return width, height
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderText prints a text using its configured style on a Surface (multi-lines are left-aligned, use label otherwise)
|
||||||
|
func (f *Font) RenderText(text string, target d2interface.Surface) error {
|
||||||
|
f.sheet.SetColorMod(f.color)
|
||||||
|
|
||||||
|
lines := strings.Split(text, "\n")
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
var (
|
||||||
|
lineHeight int
|
||||||
|
lineLength int
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, c := range line {
|
||||||
|
glyph, ok := f.glyphs[c]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := f.sheet.SetCurrentFrame(glyph.frame); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f.sheet.Render(target)
|
||||||
|
|
||||||
|
lineHeight = d2math.MaxInt(lineHeight, glyph.height)
|
||||||
|
lineLength++
|
||||||
|
|
||||||
|
target.PushTranslation(glyph.width, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
target.PopN(lineLength)
|
||||||
|
target.PushTranslation(0, lineHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
target.PopN(len(lines))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Font) initGlyphs(sr *d2datautils.StreamReader) error {
|
||||||
|
glyphs := make(map[rune]*fontGlyph)
|
||||||
|
|
||||||
|
for i := 12; i < len(f.table); i += 14 {
|
||||||
|
code, err := sr.ReadUInt16()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var glyph fontGlyph
|
||||||
|
|
||||||
|
// two bytes of 0
|
||||||
|
glyph.unknown1, err = sr.ReadBytes(unknown1BytesCount)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
width, err := sr.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
glyph.width = int(width)
|
||||||
|
|
||||||
|
height, err := sr.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
glyph.height = int(height)
|
||||||
|
|
||||||
|
// 1, 0, 0
|
||||||
|
glyph.unknown2, err = sr.ReadBytes(unknown2BytesCount)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
frame, err := sr.ReadUInt16()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
glyph.frame = int(frame)
|
||||||
|
|
||||||
|
// 1, 0, 0, character code repeated, and further 0.
|
||||||
|
glyph.unknown3, err = sr.ReadBytes(unknown3BytesCount)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
glyphs[rune(code)] = &glyph
|
||||||
|
}
|
||||||
|
|
||||||
|
f.glyphs = glyphs
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal encodes font back into byte slice
|
||||||
|
func (f *Font) Marshal() []byte {
|
||||||
|
sw := d2datautils.CreateStreamWriter()
|
||||||
|
|
||||||
|
sw.PushBytes([]byte("Woo!\x01")...)
|
||||||
|
sw.PushBytes(f.unknownHeaderBytes...)
|
||||||
|
|
||||||
|
for c, i := range f.glyphs {
|
||||||
|
sw.PushUint16(uint16(c))
|
||||||
|
sw.PushBytes(i.unknown1...)
|
||||||
|
sw.PushBytes(byte(i.width))
|
||||||
|
sw.PushBytes(byte(i.height))
|
||||||
|
sw.PushBytes(i.unknown2...)
|
||||||
|
sw.PushUint16(uint16(i.frame))
|
||||||
|
sw.PushBytes(i.unknown3...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sw.GetBytes()
|
||||||
|
}
|
@ -2,7 +2,6 @@ package d2asset
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -19,6 +18,7 @@ import (
|
|||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2records"
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2records"
|
||||||
|
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2font"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
@ -209,11 +209,11 @@ func (am *AssetManager) LoadComposite(baseType d2enum.ObjectType, token, palette
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LoadFont loads a font the resource files
|
// LoadFont loads a font the resource files
|
||||||
func (am *AssetManager) LoadFont(tablePath, spritePath, palettePath string) (*Font, error) {
|
func (am *AssetManager) LoadFont(tablePath, spritePath, palettePath string) (*d2font.Font, error) {
|
||||||
cachePath := fmt.Sprintf("%s;%s;%s", tablePath, spritePath, palettePath)
|
cachePath := fmt.Sprintf("%s;%s;%s", tablePath, spritePath, palettePath)
|
||||||
|
|
||||||
if cached, found := am.fonts.Retrieve(cachePath); found {
|
if cached, found := am.fonts.Retrieve(cachePath); found {
|
||||||
return cached.(*Font), nil
|
return cached.(*d2font.Font), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sheet, err := am.LoadAnimation(spritePath, palettePath)
|
sheet, err := am.LoadAnimation(spritePath, palettePath)
|
||||||
@ -226,18 +226,15 @@ func (am *AssetManager) LoadFont(tablePath, spritePath, palettePath string) (*Fo
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if string(tableData[:5]) != "Woo!\x01" {
|
|
||||||
return nil, fmt.Errorf("invalid font table format: %s", tablePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
am.Debugf(fmtLoadFont, tablePath, spritePath, palettePath)
|
am.Debugf(fmtLoadFont, tablePath, spritePath, palettePath)
|
||||||
|
|
||||||
font := &Font{
|
font, err := d2font.Load(tableData)
|
||||||
table: tableData,
|
if err != nil {
|
||||||
sheet: sheet,
|
return nil, fmt.Errorf("error while loading font table %s: %v", tablePath, err)
|
||||||
color: color.White,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
font.SetBackground(sheet)
|
||||||
|
|
||||||
err = am.fonts.Insert(cachePath, font, defaultCacheEntryWeight)
|
err = am.fonts.Insert(cachePath, font, defaultCacheEntryWeight)
|
||||||
|
|
||||||
return font, err
|
return font, err
|
||||||
|
@ -1,121 +0,0 @@
|
|||||||
package d2asset
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"image/color"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
|
|
||||||
)
|
|
||||||
|
|
||||||
type fontGlyph struct {
|
|
||||||
frame int
|
|
||||||
width int
|
|
||||||
height int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Font represents a displayable font
|
|
||||||
type Font struct {
|
|
||||||
sheet d2interface.Animation
|
|
||||||
table []byte
|
|
||||||
glyphs map[rune]fontGlyph
|
|
||||||
color color.Color
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetColor sets the fonts color
|
|
||||||
func (f *Font) SetColor(c color.Color) {
|
|
||||||
f.color = c
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTextMetrics returns the dimensions of the Font element in pixels
|
|
||||||
func (f *Font) GetTextMetrics(text string) (width, height int) {
|
|
||||||
if f.glyphs == nil {
|
|
||||||
f.initGlyphs()
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
lineWidth int
|
|
||||||
lineHeight int
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, c := range text {
|
|
||||||
if c == '\n' {
|
|
||||||
width = d2math.MaxInt(width, lineWidth)
|
|
||||||
height += lineHeight
|
|
||||||
lineWidth = 0
|
|
||||||
lineHeight = 0
|
|
||||||
} else if glyph, ok := f.glyphs[c]; ok {
|
|
||||||
lineWidth += glyph.width
|
|
||||||
lineHeight = d2math.MaxInt(lineHeight, glyph.height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
width = d2math.MaxInt(width, lineWidth)
|
|
||||||
height += lineHeight
|
|
||||||
|
|
||||||
return width, height
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenderText prints a text using its configured style on a Surface (multi-lines are left-aligned, use label otherwise)
|
|
||||||
func (f *Font) RenderText(text string, target d2interface.Surface) error {
|
|
||||||
if f.glyphs == nil {
|
|
||||||
f.sheet.BindRenderer(target.Renderer())
|
|
||||||
f.initGlyphs()
|
|
||||||
}
|
|
||||||
|
|
||||||
f.sheet.SetColorMod(f.color)
|
|
||||||
|
|
||||||
lines := strings.Split(text, "\n")
|
|
||||||
|
|
||||||
for _, line := range lines {
|
|
||||||
var (
|
|
||||||
lineHeight int
|
|
||||||
lineLength int
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, c := range line {
|
|
||||||
glyph, ok := f.glyphs[c]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.sheet.SetCurrentFrame(glyph.frame); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
f.sheet.Render(target)
|
|
||||||
|
|
||||||
lineHeight = d2math.MaxInt(lineHeight, glyph.height)
|
|
||||||
lineLength++
|
|
||||||
|
|
||||||
target.PushTranslation(glyph.width, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
target.PopN(lineLength)
|
|
||||||
target.PushTranslation(0, lineHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
target.PopN(len(lines))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Font) initGlyphs() {
|
|
||||||
_, maxCharHeight := f.sheet.GetFrameBounds()
|
|
||||||
|
|
||||||
glyphs := make(map[rune]fontGlyph)
|
|
||||||
|
|
||||||
for i := 12; i < len(f.table); i += 14 {
|
|
||||||
code := rune(binary.LittleEndian.Uint16(f.table[i : i+2]))
|
|
||||||
|
|
||||||
var glyph fontGlyph
|
|
||||||
glyph.frame = int(binary.LittleEndian.Uint16(f.table[i+8 : i+10]))
|
|
||||||
glyph.width = int(f.table[i+3])
|
|
||||||
glyph.height = maxCharHeight
|
|
||||||
|
|
||||||
glyphs[code] = glyph
|
|
||||||
}
|
|
||||||
|
|
||||||
f.glyphs = glyphs
|
|
||||||
}
|
|
@ -4,8 +4,8 @@ import (
|
|||||||
"image/color"
|
"image/color"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2font"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Constants defining the main shades of basic colors
|
// Constants defining the main shades of basic colors
|
||||||
@ -25,7 +25,7 @@ type Label struct {
|
|||||||
|
|
||||||
renderer d2interface.Renderer
|
renderer d2interface.Renderer
|
||||||
text string
|
text string
|
||||||
font *d2asset.Font
|
font *d2font.Font
|
||||||
surface d2interface.Surface
|
surface d2interface.Surface
|
||||||
color color.RGBA
|
color color.RGBA
|
||||||
hoverColor color.RGBA
|
hoverColor color.RGBA
|
||||||
@ -35,7 +35,7 @@ type Label struct {
|
|||||||
blinkTimer time.Time
|
blinkTimer time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func createLabel(renderer d2interface.Renderer, text string, font *d2asset.Font, col color.RGBA) (*Label, error) {
|
func createLabel(renderer d2interface.Renderer, text string, font *d2font.Font, col color.RGBA) (*Label, error) {
|
||||||
label := &Label{
|
label := &Label{
|
||||||
font: font,
|
font: font,
|
||||||
renderer: renderer,
|
renderer: renderer,
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2font"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
|
||||||
@ -532,7 +533,7 @@ func (l *Layout) createButton(renderer d2interface.Renderer, text string,
|
|||||||
return button, nil
|
return button, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Layout) loadFont(fontStyle FontStyle) (*d2asset.Font, error) {
|
func (l *Layout) loadFont(fontStyle FontStyle) (*d2font.Font, error) {
|
||||||
config := getFontStyleConfig(fontStyle)
|
config := getFontStyleConfig(fontStyle)
|
||||||
if config == nil {
|
if config == nil {
|
||||||
return nil, errors.New("invalid font style")
|
return nil, errors.New("invalid font style")
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2font"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
|
||||||
@ -19,7 +19,7 @@ type Label struct {
|
|||||||
*BaseWidget
|
*BaseWidget
|
||||||
text string
|
text string
|
||||||
Alignment HorizontalAlign
|
Alignment HorizontalAlign
|
||||||
font *d2asset.Font
|
font *d2font.Font
|
||||||
Color map[int]color.Color
|
Color map[int]color.Color
|
||||||
backgroundColor color.Color
|
backgroundColor color.Color
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user