mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-09-27 13:46:00 -04:00
Finished font rendering with color mod support.
This commit is contained in:
parent
ae3062c38b
commit
01524fdb1d
76
ColorConvert.go
Normal file
76
ColorConvert.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package OpenDiablo2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten"
|
||||||
|
)
|
||||||
|
|
||||||
|
type colorMCacheKey uint32
|
||||||
|
|
||||||
|
type colorMCacheEntry struct {
|
||||||
|
m ebiten.ColorM
|
||||||
|
atime int64
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
textM sync.Mutex
|
||||||
|
colorMCache = map[colorMCacheKey]*colorMCacheEntry{}
|
||||||
|
emptyColorM ebiten.ColorM
|
||||||
|
monotonicClock int64
|
||||||
|
cacheLimit = 512
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
emptyColorM.Scale(0, 0, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func now() int64 {
|
||||||
|
monotonicClock++
|
||||||
|
return monotonicClock
|
||||||
|
}
|
||||||
|
|
||||||
|
func ColorToColorM(clr color.Color) ebiten.ColorM {
|
||||||
|
// RGBA() is in [0 - 0xffff]. Adjust them in [0 - 0xff].
|
||||||
|
cr, cg, cb, ca := clr.RGBA()
|
||||||
|
cr >>= 8
|
||||||
|
cg >>= 8
|
||||||
|
cb >>= 8
|
||||||
|
ca >>= 8
|
||||||
|
if ca == 0 {
|
||||||
|
return emptyColorM
|
||||||
|
}
|
||||||
|
key := colorMCacheKey(uint32(cr) | (uint32(cg) << 8) | (uint32(cb) << 16) | (uint32(ca) << 24))
|
||||||
|
e, ok := colorMCache[key]
|
||||||
|
if ok {
|
||||||
|
e.atime = now()
|
||||||
|
return e.m
|
||||||
|
}
|
||||||
|
if len(colorMCache) > cacheLimit {
|
||||||
|
oldest := int64(math.MaxInt64)
|
||||||
|
oldestKey := colorMCacheKey(0)
|
||||||
|
for key, c := range colorMCache {
|
||||||
|
if c.atime < oldest {
|
||||||
|
oldestKey = key
|
||||||
|
oldest = c.atime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete(colorMCache, oldestKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
cm := ebiten.ColorM{}
|
||||||
|
rf := float64(cr) / float64(ca)
|
||||||
|
gf := float64(cg) / float64(ca)
|
||||||
|
bf := float64(cb) / float64(ca)
|
||||||
|
af := float64(ca) / 0xff
|
||||||
|
cm.Scale(rf, gf, bf, af)
|
||||||
|
e = &colorMCacheEntry{
|
||||||
|
m: cm,
|
||||||
|
atime: now(),
|
||||||
|
}
|
||||||
|
colorMCache[key] = e
|
||||||
|
|
||||||
|
return e.m
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
package OpenDiablo2
|
package OpenDiablo2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"image/color"
|
||||||
|
|
||||||
"github.com/essial/OpenDiablo2/ResourcePaths"
|
"github.com/essial/OpenDiablo2/ResourcePaths"
|
||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
)
|
)
|
||||||
@ -14,6 +16,7 @@ type MainMenu struct {
|
|||||||
DiabloLogoLeftBack Sprite
|
DiabloLogoLeftBack Sprite
|
||||||
DiabloLogoRightBack Sprite
|
DiabloLogoRightBack Sprite
|
||||||
CopyrightLabel *UILabel
|
CopyrightLabel *UILabel
|
||||||
|
CopyrightLabel2 *UILabel
|
||||||
ShowTrademarkScreen bool
|
ShowTrademarkScreen bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,34 +31,58 @@ func CreateMainMenu(engine *Engine) *MainMenu {
|
|||||||
|
|
||||||
func (v *MainMenu) Load() {
|
func (v *MainMenu) Load() {
|
||||||
go func() {
|
go func() {
|
||||||
loadStep := 1.0 / 7.0
|
loadStep := 1.0 / 8.0
|
||||||
v.Engine.LoadingProgress = 0
|
v.Engine.LoadingProgress = 0
|
||||||
v.CopyrightLabel = CreateUILabel(v.Engine, ResourcePaths.FontFormal11, "static")
|
{
|
||||||
v.CopyrightLabel.SetText("Hello, world!")
|
v.CopyrightLabel = CreateUILabel(v.Engine, ResourcePaths.FontFormal12, "static")
|
||||||
v.CopyrightLabel.MoveTo(0, 0)
|
v.CopyrightLabel.Alignment = UILabelAlignCenter
|
||||||
|
v.CopyrightLabel.SetText("Diablo 2 is © Copyright 2000-2016 Blizzard Entertainment")
|
||||||
|
v.CopyrightLabel.ColorMod = color.RGBA{188, 168, 140, 255}
|
||||||
|
v.CopyrightLabel.MoveTo(400, 500)
|
||||||
v.Engine.LoadingProgress += loadStep
|
v.Engine.LoadingProgress += loadStep
|
||||||
|
}
|
||||||
|
{
|
||||||
|
v.CopyrightLabel2 = CreateUILabel(v.Engine, ResourcePaths.FontFormal12, "static")
|
||||||
|
v.CopyrightLabel2.Alignment = UILabelAlignCenter
|
||||||
|
v.CopyrightLabel2.SetText("All Rights Reserved.")
|
||||||
|
v.CopyrightLabel2.ColorMod = color.RGBA{188, 168, 140, 255}
|
||||||
|
v.CopyrightLabel2.MoveTo(400, 525)
|
||||||
|
v.Engine.LoadingProgress += loadStep
|
||||||
|
}
|
||||||
|
{
|
||||||
v.Background = v.Engine.LoadSprite(ResourcePaths.GameSelectScreen, v.Engine.Palettes["sky"])
|
v.Background = v.Engine.LoadSprite(ResourcePaths.GameSelectScreen, v.Engine.Palettes["sky"])
|
||||||
v.Background.MoveTo(0, 0)
|
v.Background.MoveTo(0, 0)
|
||||||
v.Engine.LoadingProgress += loadStep
|
v.Engine.LoadingProgress += loadStep
|
||||||
|
}
|
||||||
|
{
|
||||||
v.TrademarkBackground = v.Engine.LoadSprite(ResourcePaths.TrademarkScreen, v.Engine.Palettes["sky"])
|
v.TrademarkBackground = v.Engine.LoadSprite(ResourcePaths.TrademarkScreen, v.Engine.Palettes["sky"])
|
||||||
v.TrademarkBackground.MoveTo(0, 0)
|
v.TrademarkBackground.MoveTo(0, 0)
|
||||||
v.Engine.LoadingProgress += loadStep
|
v.Engine.LoadingProgress += loadStep
|
||||||
|
}
|
||||||
|
{
|
||||||
v.DiabloLogoLeft = v.Engine.LoadSprite(ResourcePaths.Diablo2LogoFireLeft, v.Engine.Palettes["units"])
|
v.DiabloLogoLeft = v.Engine.LoadSprite(ResourcePaths.Diablo2LogoFireLeft, v.Engine.Palettes["units"])
|
||||||
v.DiabloLogoLeft.Blend = true
|
v.DiabloLogoLeft.Blend = true
|
||||||
v.DiabloLogoLeft.Animate = true
|
v.DiabloLogoLeft.Animate = true
|
||||||
v.DiabloLogoLeft.MoveTo(400, 120)
|
v.DiabloLogoLeft.MoveTo(400, 120)
|
||||||
v.Engine.LoadingProgress += loadStep
|
v.Engine.LoadingProgress += loadStep
|
||||||
|
}
|
||||||
|
{
|
||||||
v.DiabloLogoRight = v.Engine.LoadSprite(ResourcePaths.Diablo2LogoFireRight, v.Engine.Palettes["units"])
|
v.DiabloLogoRight = v.Engine.LoadSprite(ResourcePaths.Diablo2LogoFireRight, v.Engine.Palettes["units"])
|
||||||
v.DiabloLogoRight.Blend = true
|
v.DiabloLogoRight.Blend = true
|
||||||
v.DiabloLogoRight.Animate = true
|
v.DiabloLogoRight.Animate = true
|
||||||
v.DiabloLogoRight.MoveTo(400, 120)
|
v.DiabloLogoRight.MoveTo(400, 120)
|
||||||
v.Engine.LoadingProgress += loadStep
|
v.Engine.LoadingProgress += loadStep
|
||||||
|
}
|
||||||
|
{
|
||||||
v.DiabloLogoLeftBack = v.Engine.LoadSprite(ResourcePaths.Diablo2LogoBlackLeft, v.Engine.Palettes["units"])
|
v.DiabloLogoLeftBack = v.Engine.LoadSprite(ResourcePaths.Diablo2LogoBlackLeft, v.Engine.Palettes["units"])
|
||||||
v.DiabloLogoLeftBack.MoveTo(400, 120)
|
v.DiabloLogoLeftBack.MoveTo(400, 120)
|
||||||
v.Engine.LoadingProgress += loadStep
|
v.Engine.LoadingProgress += loadStep
|
||||||
|
}
|
||||||
|
{
|
||||||
v.DiabloLogoRightBack = v.Engine.LoadSprite(ResourcePaths.Diablo2LogoBlackRight, v.Engine.Palettes["units"])
|
v.DiabloLogoRightBack = v.Engine.LoadSprite(ResourcePaths.Diablo2LogoBlackRight, v.Engine.Palettes["units"])
|
||||||
v.DiabloLogoRightBack.MoveTo(400, 120)
|
v.DiabloLogoRightBack.MoveTo(400, 120)
|
||||||
v.Engine.LoadingProgress = 1.0
|
v.Engine.LoadingProgress = 1.0
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,6 +103,7 @@ func (v *MainMenu) Render(screen *ebiten.Image) {
|
|||||||
|
|
||||||
if v.ShowTrademarkScreen {
|
if v.ShowTrademarkScreen {
|
||||||
v.CopyrightLabel.Draw(screen)
|
v.CopyrightLabel.Draw(screen)
|
||||||
|
v.CopyrightLabel2.Draw(screen)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
13
Sprite.go
13
Sprite.go
@ -2,6 +2,7 @@ package OpenDiablo2
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"image/color"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
@ -16,6 +17,7 @@ type Sprite struct {
|
|||||||
Blend bool
|
Blend bool
|
||||||
LastFrameTime time.Time
|
LastFrameTime time.Time
|
||||||
Animate bool
|
Animate bool
|
||||||
|
ColorMod color.Color
|
||||||
}
|
}
|
||||||
|
|
||||||
type SpriteFrame struct {
|
type SpriteFrame struct {
|
||||||
@ -38,6 +40,7 @@ func CreateSprite(data []byte, palette Palette) Sprite {
|
|||||||
Frame: 0,
|
Frame: 0,
|
||||||
Direction: 0,
|
Direction: 0,
|
||||||
Blend: false,
|
Blend: false,
|
||||||
|
ColorMod: nil,
|
||||||
Directions: binary.LittleEndian.Uint32(data[16:20]),
|
Directions: binary.LittleEndian.Uint32(data[16:20]),
|
||||||
FramesPerDirection: binary.LittleEndian.Uint32(data[20:24]),
|
FramesPerDirection: binary.LittleEndian.Uint32(data[20:24]),
|
||||||
Animate: false,
|
Animate: false,
|
||||||
@ -149,7 +152,9 @@ func (v *Sprite) Draw(target *ebiten.Image) {
|
|||||||
if v.Blend {
|
if v.Blend {
|
||||||
opts.CompositeMode = ebiten.CompositeModeLighter
|
opts.CompositeMode = ebiten.CompositeModeLighter
|
||||||
}
|
}
|
||||||
//opts.ColorM.ChangeHSV(0.0, 1.0, 0.9)
|
if v.ColorMod != nil {
|
||||||
|
opts.ColorM = ColorToColorM(v.ColorMod)
|
||||||
|
}
|
||||||
target.DrawImage(frame.Image, opts)
|
target.DrawImage(frame.Image, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,6 +171,12 @@ func (v *Sprite) DrawSegments(target *ebiten.Image, xSegments, ySegments, offset
|
|||||||
float64(int32(v.X)+frame.OffsetX+xOffset),
|
float64(int32(v.X)+frame.OffsetX+xOffset),
|
||||||
float64(int32(v.Y)+frame.OffsetY+yOffset),
|
float64(int32(v.Y)+frame.OffsetY+yOffset),
|
||||||
)
|
)
|
||||||
|
if v.Blend {
|
||||||
|
opts.CompositeMode = ebiten.CompositeModeLighter
|
||||||
|
}
|
||||||
|
if v.ColorMod != nil {
|
||||||
|
opts.ColorM = ColorToColorM(v.ColorMod)
|
||||||
|
}
|
||||||
target.DrawImage(frame.Image, opts)
|
target.DrawImage(frame.Image, opts)
|
||||||
xOffset += int32(frame.Width)
|
xOffset += int32(frame.Width)
|
||||||
biggestYOffset = MaxInt32(biggestYOffset, int32(frame.Height))
|
biggestYOffset = MaxInt32(biggestYOffset, int32(frame.Height))
|
||||||
|
26
UILabel.go
26
UILabel.go
@ -1,22 +1,36 @@
|
|||||||
package OpenDiablo2
|
package OpenDiablo2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"image/color"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type UILabelAlignment uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
UILabelAlignLeft UILabelAlignment = 0
|
||||||
|
UILabelAlignCenter UILabelAlignment = 1
|
||||||
|
UILabelAlignRight UILabelAlignment = 2
|
||||||
|
)
|
||||||
|
|
||||||
type UILabel struct {
|
type UILabel struct {
|
||||||
text string
|
text string
|
||||||
X int
|
X int
|
||||||
Y int
|
Y int
|
||||||
Width uint32
|
Width uint32
|
||||||
Height uint32
|
Height uint32
|
||||||
|
Alignment UILabelAlignment
|
||||||
font *MPQFont
|
font *MPQFont
|
||||||
imageData *ebiten.Image
|
imageData *ebiten.Image
|
||||||
|
ColorMod color.Color
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateUILabel creates a new instance of a UI label
|
// CreateUILabel creates a new instance of a UI label
|
||||||
func CreateUILabel(engine *Engine, font, palette string) *UILabel {
|
func CreateUILabel(engine *Engine, font, palette string) *UILabel {
|
||||||
result := &UILabel{
|
result := &UILabel{
|
||||||
|
Alignment: UILabelAlignLeft,
|
||||||
|
ColorMod: nil,
|
||||||
font: engine.GetFont(font, palette),
|
font: engine.GetFont(font, palette),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +44,14 @@ func (v *UILabel) Draw(target *ebiten.Image) {
|
|||||||
}
|
}
|
||||||
v.cacheImage()
|
v.cacheImage()
|
||||||
opts := &ebiten.DrawImageOptions{}
|
opts := &ebiten.DrawImageOptions{}
|
||||||
|
|
||||||
|
if v.Alignment == UILabelAlignCenter {
|
||||||
|
opts.GeoM.Translate(float64(v.X-int(v.Width/2)), float64(v.Y))
|
||||||
|
} else if v.Alignment == UILabelAlignRight {
|
||||||
|
opts.GeoM.Translate(float64(v.X-int(v.Width)), float64(v.Y))
|
||||||
|
} else {
|
||||||
opts.GeoM.Translate(float64(v.X), float64(v.Y))
|
opts.GeoM.Translate(float64(v.X), float64(v.Y))
|
||||||
|
}
|
||||||
opts.CompositeMode = ebiten.CompositeModeSourceAtop
|
opts.CompositeMode = ebiten.CompositeModeSourceAtop
|
||||||
opts.Filter = ebiten.FilterNearest
|
opts.Filter = ebiten.FilterNearest
|
||||||
target.DrawImage(v.imageData, opts)
|
target.DrawImage(v.imageData, opts)
|
||||||
@ -39,7 +60,7 @@ func (v *UILabel) Draw(target *ebiten.Image) {
|
|||||||
func (v *UILabel) calculateSize() (uint32, uint32) {
|
func (v *UILabel) calculateSize() (uint32, uint32) {
|
||||||
width := uint32(0)
|
width := uint32(0)
|
||||||
height := uint32(0)
|
height := uint32(0)
|
||||||
for ch := range v.text {
|
for _, ch := range v.text {
|
||||||
metric := v.font.Metrics[uint8(ch)]
|
metric := v.font.Metrics[uint8(ch)]
|
||||||
width += uint32(metric.Width)
|
width += uint32(metric.Width)
|
||||||
height = Max(height, uint32(metric.Height))
|
height = Max(height, uint32(metric.Height))
|
||||||
@ -61,11 +82,12 @@ func (v *UILabel) cacheImage() {
|
|||||||
v.Height = height
|
v.Height = height
|
||||||
v.imageData, _ = ebiten.NewImage(int(width), int(height), ebiten.FilterNearest)
|
v.imageData, _ = ebiten.NewImage(int(width), int(height), ebiten.FilterNearest)
|
||||||
x := uint32(0)
|
x := uint32(0)
|
||||||
|
v.font.FontSprite.ColorMod = v.ColorMod
|
||||||
for _, ch := range v.text {
|
for _, ch := range v.text {
|
||||||
char := uint8(ch)
|
char := uint8(ch)
|
||||||
metric := v.font.Metrics[char]
|
metric := v.font.Metrics[char]
|
||||||
v.font.FontSprite.Frame = char
|
v.font.FontSprite.Frame = char
|
||||||
v.font.FontSprite.MoveTo(v.X+int(x), int(v.Height))
|
v.font.FontSprite.MoveTo(int(x), int(height))
|
||||||
v.font.FontSprite.Draw(v.imageData)
|
v.font.FontSprite.Draw(v.imageData)
|
||||||
x += uint32(metric.Width)
|
x += uint32(metric.Width)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user