text: Optimization for browsers

This commit is contained in:
Hajime Hoshi 2018-02-12 01:08:53 +09:00
parent 950219f5b6
commit 9118772fd1

View File

@ -60,30 +60,32 @@ func fontFaceToFace(f font.Face) font.Face {
} }
var ( var (
charBounds = map[font.Face]map[rune]fixed.Rectangle26_6{} // Use pointers to avoid copying on browsers.
charBounds = map[font.Face]map[rune]*fixed.Rectangle26_6{}
) )
type char struct { type char struct {
face font.Face face font.Face
rune rune rune rune
atlasG int
} }
func (c *char) bounds() (minX, minY, maxX, maxY fixed.Int26_6) { func (c *char) bounds() *fixed.Rectangle26_6 {
if m, ok := charBounds[c.face]; ok { if m, ok := charBounds[c.face]; ok {
if b, ok := m[c.rune]; ok { if b, ok := m[c.rune]; ok {
return b.Min.X, b.Min.Y, b.Max.X, b.Max.Y return b
} }
} else { } else {
charBounds[c.face] = map[rune]fixed.Rectangle26_6{} charBounds[c.face] = map[rune]*fixed.Rectangle26_6{}
} }
b, _, _ := c.face.GlyphBounds(c.rune) b, _, _ := c.face.GlyphBounds(c.rune)
charBounds[c.face][c.rune] = b charBounds[c.face][c.rune] = &b
return b.Min.X, b.Min.Y, b.Max.X, b.Max.Y return &b
} }
func (c *char) size() (fixed.Int26_6, fixed.Int26_6) { func (c *char) size() (fixed.Int26_6, fixed.Int26_6) {
minX, minY, maxX, maxY := c.bounds() b := c.bounds()
return maxX - minX, maxY - minY return b.Max.X - b.Min.X, b.Max.Y - b.Min.Y
} }
func (c *char) empty() bool { func (c *char) empty() bool {
@ -92,6 +94,10 @@ func (c *char) empty() bool {
} }
func (c *char) atlasGroup() int { func (c *char) atlasGroup() int {
if c.atlasG != 0 {
return c.atlasG
}
x, y := c.size() x, y := c.size()
w, h := x.Ceil(), y.Ceil() w, h := x.Ceil(), y.Ceil()
t := w t := w
@ -104,7 +110,8 @@ func (c *char) atlasGroup() int {
if t < 32 { if t < 32 {
return 32 return 32
} }
return emath.NextPowerOf2Int(t) c.atlasG = emath.NextPowerOf2Int(t)
return c.atlasG
} }
type glyph struct { type glyph struct {
@ -123,9 +130,9 @@ func (g *glyph) draw(dst *ebiten.Image, x, y fixed.Int26_6, clr color.Color) {
return return
} }
minX, minY, _, _ := g.char.bounds() b := g.char.bounds()
op := &ebiten.DrawImageOptions{} op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(fixed26_6ToFloat64(x+minX), fixed26_6ToFloat64(y+minY)) op.GeoM.Translate(fixed26_6ToFloat64(x+b.Min.X), fixed26_6ToFloat64(y+b.Min.Y))
rf := float64(cr) / float64(ca) rf := float64(cr) / float64(ca)
gf := float64(cg) / float64(ca) gf := float64(cg) / float64(ca)
@ -176,9 +183,9 @@ func (a *atlas) maxGlyphNum() int {
return xnum * ynum return xnum * ynum
} }
func (a *atlas) appendGlyph(face font.Face, rune rune, now int64) *glyph { func (a *atlas) appendGlyph(char char, now int64) *glyph {
g := &glyph{ g := &glyph{
char: char{face, rune}, char: char,
atime: now, atime: now,
} }
if len(a.runeToGlyph) == a.maxGlyphNum() { if len(a.runeToGlyph) == a.maxGlyphNum() {
@ -216,8 +223,8 @@ func (a *atlas) draw(glyph *glyph) {
Src: image.White, Src: image.White,
Face: glyph.char.face, Face: glyph.char.face,
} }
minX, minY, _, _ := glyph.char.bounds() b := glyph.char.bounds()
d.Dot = fixed.Point26_6{-minX, -minY} d.Dot = fixed.Point26_6{-b.Min.X, -b.Min.Y}
d.DrawString(string(glyph.char.rune)) d.DrawString(string(glyph.char.rune))
a.tmpImage.ReplacePixels(dst.Pix) a.tmpImage.ReplacePixels(dst.Pix)
@ -231,7 +238,10 @@ func (a *atlas) draw(glyph *glyph) {
} }
func getGlyphFromCache(face font.Face, r rune, now int64) *glyph { func getGlyphFromCache(face font.Face, r rune, now int64) *glyph {
ch := char{face, r} ch := char{
face: face,
rune: r,
}
var at *atlas var at *atlas
if m, ok := atlases[face]; ok { if m, ok := atlases[face]; ok {
a, ok := m[ch.atlasGroup()] a, ok := m[ch.atlasGroup()]
@ -277,7 +287,7 @@ func getGlyphFromCache(face font.Face, r rune, now int64) *glyph {
atlases[face][ch.atlasGroup()] = at atlases[face][ch.atlasGroup()] = at
} }
return at.appendGlyph(ch.face, ch.rune, now) return at.appendGlyph(ch, now)
} }
var textM sync.Mutex var textM sync.Mutex