mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-26 02:42:02 +01:00
text: Optimize speed: Don't use a struct for map keys
Especially for browsers
This commit is contained in:
parent
a5f9382878
commit
da51377cc5
54
text/text.go
54
text/text.go
@ -65,7 +65,7 @@ func fontFaceToFace(f font.Face) face {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
charBounds = map[char]fixed.Rectangle26_6{}
|
charBounds = map[font.Face]map[rune]fixed.Rectangle26_6{}
|
||||||
)
|
)
|
||||||
|
|
||||||
type char struct {
|
type char struct {
|
||||||
@ -74,11 +74,15 @@ type char struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *char) bounds() fixed.Rectangle26_6 {
|
func (c *char) bounds() fixed.Rectangle26_6 {
|
||||||
if b, ok := charBounds[*c]; ok {
|
if m, ok := charBounds[c.face.f]; ok {
|
||||||
return b
|
if b, ok := m[c.rune]; ok {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
charBounds[c.face.f] = map[rune]fixed.Rectangle26_6{}
|
||||||
}
|
}
|
||||||
b, _, _ := c.face.f.GlyphBounds(c.rune)
|
b, _, _ := c.face.f.GlyphBounds(c.rune)
|
||||||
charBounds[*c] = b
|
charBounds[c.face.f][c.rune] = b
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +139,7 @@ func (g *glyph) draw(dst *ebiten.Image, x, y fixed.Int26_6, clr color.Color) {
|
|||||||
af := float64(ca) / 0xffff
|
af := float64(ca) / 0xffff
|
||||||
op.ColorM.Scale(rf, gf, bf, af)
|
op.ColorM.Scale(rf, gf, bf, af)
|
||||||
|
|
||||||
a := atlases[g.char.atlasGroup()]
|
a := atlases[g.char.face.f][g.char.atlasGroup()]
|
||||||
sx, sy := a.at(g)
|
sx, sy := a.at(g)
|
||||||
r := image.Rect(sx, sy, sx+a.glyphSize, sy+a.glyphSize)
|
r := image.Rect(sx, sy, sx+a.glyphSize, sy+a.glyphSize)
|
||||||
op.SourceRect = &r
|
op.SourceRect = &r
|
||||||
@ -144,7 +148,7 @@ func (g *glyph) draw(dst *ebiten.Image, x, y fixed.Int26_6, clr color.Color) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
atlases = map[int]*atlas{}
|
atlases = map[font.Face]map[int]*atlas{}
|
||||||
)
|
)
|
||||||
|
|
||||||
type atlas struct {
|
type atlas struct {
|
||||||
@ -158,7 +162,7 @@ type atlas struct {
|
|||||||
// This value is always power of 2.
|
// This value is always power of 2.
|
||||||
glyphSize int
|
glyphSize int
|
||||||
|
|
||||||
charToGlyph map[char]*glyph
|
runeToGlyph map[rune]*glyph
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *atlas) at(glyph *glyph) (int, int) {
|
func (a *atlas) at(glyph *glyph) (int, int) {
|
||||||
@ -183,10 +187,10 @@ func (a *atlas) appendGlyph(face face, rune rune, now int64) *glyph {
|
|||||||
char: char{face, rune},
|
char: char{face, rune},
|
||||||
atime: now,
|
atime: now,
|
||||||
}
|
}
|
||||||
if len(a.charToGlyph) == a.maxGlyphNum() {
|
if len(a.runeToGlyph) == a.maxGlyphNum() {
|
||||||
var oldest *glyph
|
var oldest *glyph
|
||||||
t := int64(math.MaxInt64)
|
t := int64(math.MaxInt64)
|
||||||
for _, g := range a.charToGlyph {
|
for _, g := range a.runeToGlyph {
|
||||||
if g.atime < t {
|
if g.atime < t {
|
||||||
t = g.atime
|
t = g.atime
|
||||||
oldest = g
|
oldest = g
|
||||||
@ -196,13 +200,13 @@ func (a *atlas) appendGlyph(face face, rune rune, now int64) *glyph {
|
|||||||
panic("not reached")
|
panic("not reached")
|
||||||
}
|
}
|
||||||
idx := oldest.index
|
idx := oldest.index
|
||||||
delete(a.charToGlyph, oldest.char)
|
delete(a.runeToGlyph, oldest.char.rune)
|
||||||
|
|
||||||
g.index = idx
|
g.index = idx
|
||||||
} else {
|
} else {
|
||||||
g.index = len(a.charToGlyph)
|
g.index = len(a.runeToGlyph)
|
||||||
}
|
}
|
||||||
a.charToGlyph[g.char] = g
|
a.runeToGlyph[g.char.rune] = g
|
||||||
a.draw(g)
|
a.draw(g)
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
@ -234,13 +238,19 @@ func (a *atlas) draw(glyph *glyph) {
|
|||||||
|
|
||||||
func getGlyphFromCache(face face, r rune, now int64) *glyph {
|
func getGlyphFromCache(face face, r rune, now int64) *glyph {
|
||||||
ch := char{face, r}
|
ch := char{face, r}
|
||||||
a, ok := atlases[ch.atlasGroup()]
|
var at *atlas
|
||||||
if ok {
|
if m, ok := atlases[face.f]; ok {
|
||||||
g, ok := a.charToGlyph[ch]
|
a, ok := m[ch.atlasGroup()]
|
||||||
if ok {
|
if ok {
|
||||||
g.atime = now
|
g, ok := a.runeToGlyph[r]
|
||||||
return g
|
if ok {
|
||||||
|
g.atime = now
|
||||||
|
return g
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
at = a
|
||||||
|
} else {
|
||||||
|
atlases[face.f] = map[int]*atlas{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ch.empty() {
|
if ch.empty() {
|
||||||
@ -252,7 +262,7 @@ func getGlyphFromCache(face face, r rune, now int64) *glyph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok {
|
if at == nil {
|
||||||
// Don't use ebiten.MaxImageSize here.
|
// Don't use ebiten.MaxImageSize here.
|
||||||
// It's because the back-end image pixels will be restored from GPU
|
// It's because the back-end image pixels will be restored from GPU
|
||||||
// whenever a new glyph is rendered on the image, and restoring cost is
|
// whenever a new glyph is rendered on the image, and restoring cost is
|
||||||
@ -265,15 +275,15 @@ func getGlyphFromCache(face face, r rune, now int64) *glyph {
|
|||||||
// TODO: How about making a new function for 'flagile' image?
|
// TODO: How about making a new function for 'flagile' image?
|
||||||
const size = 1024
|
const size = 1024
|
||||||
i, _ := ebiten.NewImage(size, size, ebiten.FilterNearest)
|
i, _ := ebiten.NewImage(size, size, ebiten.FilterNearest)
|
||||||
a = &atlas{
|
at = &atlas{
|
||||||
image: i,
|
image: i,
|
||||||
glyphSize: ch.atlasGroup(),
|
glyphSize: ch.atlasGroup(),
|
||||||
charToGlyph: map[char]*glyph{},
|
runeToGlyph: map[rune]*glyph{},
|
||||||
}
|
}
|
||||||
atlases[ch.atlasGroup()] = a
|
atlases[face.f][ch.atlasGroup()] = at
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.appendGlyph(ch.face, ch.rune, now)
|
return at.appendGlyph(ch.face, ch.rune, now)
|
||||||
}
|
}
|
||||||
|
|
||||||
var textM sync.Mutex
|
var textM sync.Mutex
|
||||||
|
Loading…
Reference in New Issue
Block a user