1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-17 21:05:22 +00:00

Merge pull request #1053 from gucio321/date-encoder-font

Date encoder: font table
This commit is contained in:
gravestench 2021-02-16 22:59:10 -08:00 committed by GitHub
commit 6a94dfcfcf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 253 additions and 138 deletions

View File

@ -0,0 +1,3 @@
// Package d2font contains logic for loading and processing
// d2 fonts
package d2font

View 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()
}

View File

@ -2,7 +2,6 @@ package d2asset
import (
"fmt"
"image/color"
"io"
"io/ioutil"
"path/filepath"
@ -19,6 +18,7 @@ import (
"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/d2enum"
@ -209,11 +209,11 @@ func (am *AssetManager) LoadComposite(baseType d2enum.ObjectType, token, palette
}
// 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)
if cached, found := am.fonts.Retrieve(cachePath); found {
return cached.(*Font), nil
return cached.(*d2font.Font), nil
}
sheet, err := am.LoadAnimation(spritePath, palettePath)
@ -226,18 +226,15 @@ func (am *AssetManager) LoadFont(tablePath, spritePath, palettePath string) (*Fo
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)
font := &Font{
table: tableData,
sheet: sheet,
color: color.White,
font, err := d2font.Load(tableData)
if err != nil {
return nil, fmt.Errorf("error while loading font table %s: %v", tablePath, err)
}
font.SetBackground(sheet)
err = am.fonts.Insert(cachePath, font, defaultCacheEntryWeight)
return font, err

View File

@ -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
}

View File

@ -4,8 +4,8 @@ import (
"image/color"
"time"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2font"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
)
// Constants defining the main shades of basic colors
@ -25,7 +25,7 @@ type Label struct {
renderer d2interface.Renderer
text string
font *d2asset.Font
font *d2font.Font
surface d2interface.Surface
color color.RGBA
hoverColor color.RGBA
@ -35,7 +35,7 @@ type Label struct {
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{
font: font,
renderer: renderer,

View File

@ -4,6 +4,7 @@ import (
"errors"
"image/color"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2font"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
@ -532,7 +533,7 @@ func (l *Layout) createButton(renderer d2interface.Renderer, text string,
return button, nil
}
func (l *Layout) loadFont(fontStyle FontStyle) (*d2asset.Font, error) {
func (l *Layout) loadFont(fontStyle FontStyle) (*d2font.Font, error) {
config := getFontStyleConfig(fontStyle)
if config == nil {
return nil, errors.New("invalid font style")

View File

@ -5,7 +5,7 @@ import (
"regexp"
"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/d2util"
@ -19,7 +19,7 @@ type Label struct {
*BaseWidget
text string
Alignment HorizontalAlign
font *d2asset.Font
font *d2font.Font
Color map[int]color.Color
backgroundColor color.Color