mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-24 01:42:05 +01:00
text: refactoring: introduce faceWithCache
This commit is contained in:
parent
8de08295e9
commit
e374ca0ac3
@ -1,38 +0,0 @@
|
|||||||
// Copyright 2018 The Ebiten Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package text
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/image/font"
|
|
||||||
"golang.org/x/image/math/fixed"
|
|
||||||
)
|
|
||||||
|
|
||||||
var glyphAdvanceCache = map[font.Face]map[rune]fixed.Int26_6{}
|
|
||||||
|
|
||||||
func glyphAdvance(face font.Face, r rune) fixed.Int26_6 {
|
|
||||||
m, ok := glyphAdvanceCache[face]
|
|
||||||
if !ok {
|
|
||||||
m = map[rune]fixed.Int26_6{}
|
|
||||||
glyphAdvanceCache[face] = m
|
|
||||||
}
|
|
||||||
|
|
||||||
a, ok := m[r]
|
|
||||||
if !ok {
|
|
||||||
a, _ = face.GlyphAdvance(r)
|
|
||||||
m[r] = a
|
|
||||||
}
|
|
||||||
|
|
||||||
return a
|
|
||||||
}
|
|
114
text/cache.go
Normal file
114
text/cache.go
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// Copyright 2023 The Ebitengine Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package text
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
|
||||||
|
"golang.org/x/image/font"
|
||||||
|
"golang.org/x/image/math/fixed"
|
||||||
|
)
|
||||||
|
|
||||||
|
type glyphBoundsCacheValue struct {
|
||||||
|
bounds fixed.Rectangle26_6
|
||||||
|
advance fixed.Int26_6
|
||||||
|
ok bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type glyphAdvanceCacheValue struct {
|
||||||
|
advance fixed.Int26_6
|
||||||
|
ok bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type faceWithCache struct {
|
||||||
|
f font.Face
|
||||||
|
|
||||||
|
glyphBoundsCache map[rune]glyphBoundsCacheValue
|
||||||
|
glyphAdvanceCache map[rune]glyphAdvanceCacheValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *faceWithCache) Close() error {
|
||||||
|
if err := f.f.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f.glyphBoundsCache = nil
|
||||||
|
f.glyphAdvanceCache = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var faceWithCacheCache map[font.Face]*faceWithCache
|
||||||
|
|
||||||
|
func faceWithCacheFromFace(face font.Face) *faceWithCache {
|
||||||
|
if f, ok := faceWithCacheCache[face]; ok {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
f := &faceWithCache{
|
||||||
|
f: face,
|
||||||
|
}
|
||||||
|
if faceWithCacheCache == nil {
|
||||||
|
faceWithCacheCache = map[font.Face]*faceWithCache{}
|
||||||
|
}
|
||||||
|
faceWithCacheCache[face] = f
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *faceWithCache) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
|
||||||
|
// TODO: Move glyphImageCache to here.
|
||||||
|
return f.f.Glyph(dot, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *faceWithCache) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
|
||||||
|
if v, ok := f.glyphBoundsCache[r]; ok {
|
||||||
|
return v.bounds, v.advance, v.ok
|
||||||
|
}
|
||||||
|
|
||||||
|
bounds, advance, ok = f.f.GlyphBounds(r)
|
||||||
|
if f.glyphBoundsCache == nil {
|
||||||
|
f.glyphBoundsCache = map[rune]glyphBoundsCacheValue{}
|
||||||
|
}
|
||||||
|
f.glyphBoundsCache[r] = glyphBoundsCacheValue{
|
||||||
|
bounds: bounds,
|
||||||
|
advance: advance,
|
||||||
|
ok: ok,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *faceWithCache) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
|
||||||
|
if v, ok := f.glyphAdvanceCache[r]; ok {
|
||||||
|
return v.advance, v.ok
|
||||||
|
}
|
||||||
|
|
||||||
|
advance, ok = f.f.GlyphAdvance(r)
|
||||||
|
if f.glyphAdvanceCache == nil {
|
||||||
|
f.glyphAdvanceCache = map[rune]glyphAdvanceCacheValue{}
|
||||||
|
}
|
||||||
|
f.glyphAdvanceCache[r] = glyphAdvanceCacheValue{
|
||||||
|
advance: advance,
|
||||||
|
ok: ok,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *faceWithCache) Kern(r0, r1 rune) fixed.Int26_6 {
|
||||||
|
// TODO: Cache the values (#2673).
|
||||||
|
return f.f.Kern(r0, r1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *faceWithCache) Metrics() font.Metrics {
|
||||||
|
return f.f.Metrics()
|
||||||
|
}
|
72
text/text.go
72
text/text.go
@ -71,22 +71,6 @@ func drawGlyph(dst *ebiten.Image, img *ebiten.Image, topleft fixed.Point26_6, op
|
|||||||
dst.DrawImage(img, op2)
|
dst.DrawImage(img, op2)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
glyphBoundsCache = map[font.Face]map[rune]fixed.Rectangle26_6{}
|
|
||||||
)
|
|
||||||
|
|
||||||
func getGlyphBounds(face font.Face, r rune) fixed.Rectangle26_6 {
|
|
||||||
if _, ok := glyphBoundsCache[face]; !ok {
|
|
||||||
glyphBoundsCache[face] = map[rune]fixed.Rectangle26_6{}
|
|
||||||
}
|
|
||||||
if b, ok := glyphBoundsCache[face][r]; ok {
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
b, _, _ := face.GlyphBounds(r)
|
|
||||||
glyphBoundsCache[face][r] = b
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
type glyphImageCacheKey struct {
|
type glyphImageCacheKey struct {
|
||||||
rune rune
|
rune rune
|
||||||
xoffset fixed.Int26_6
|
xoffset fixed.Int26_6
|
||||||
@ -98,10 +82,10 @@ type glyphImageCacheEntry struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
glyphImageCache = map[font.Face]map[glyphImageCacheKey]*glyphImageCacheEntry{}
|
glyphImageCache = map[*faceWithCache]map[glyphImageCacheKey]*glyphImageCacheEntry{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func getGlyphImage(face font.Face, r rune, offset fixed.Point26_6) *ebiten.Image {
|
func getGlyphImage(face *faceWithCache, r rune, offset fixed.Point26_6) *ebiten.Image {
|
||||||
if _, ok := glyphImageCache[face]; !ok {
|
if _, ok := glyphImageCache[face]; !ok {
|
||||||
glyphImageCache[face] = map[glyphImageCacheKey]*glyphImageCacheEntry{}
|
glyphImageCache[face] = map[glyphImageCacheKey]*glyphImageCacheEntry{}
|
||||||
}
|
}
|
||||||
@ -115,7 +99,7 @@ func getGlyphImage(face font.Face, r rune, offset fixed.Point26_6) *ebiten.Image
|
|||||||
return e.image
|
return e.image
|
||||||
}
|
}
|
||||||
|
|
||||||
b := getGlyphBounds(face, r)
|
b, _, _ := face.GlyphBounds(r)
|
||||||
w, h := (b.Max.X - b.Min.X).Ceil(), (b.Max.Y - b.Min.Y).Ceil()
|
w, h := (b.Max.X - b.Min.X).Ceil(), (b.Max.Y - b.Min.Y).Ceil()
|
||||||
if w == 0 || h == 0 {
|
if w == 0 || h == 0 {
|
||||||
glyphImageCache[face][key] = &glyphImageCacheEntry{
|
glyphImageCache[face][key] = &glyphImageCacheEntry{
|
||||||
@ -235,14 +219,16 @@ func DrawWithOptions(dst *ebiten.Image, text string, face font.Face, options *eb
|
|||||||
textM.Lock()
|
textM.Lock()
|
||||||
defer textM.Unlock()
|
defer textM.Unlock()
|
||||||
|
|
||||||
|
fc := faceWithCacheFromFace(face)
|
||||||
|
|
||||||
var dx, dy fixed.Int26_6
|
var dx, dy fixed.Int26_6
|
||||||
prevR := rune(-1)
|
prevR := rune(-1)
|
||||||
|
|
||||||
faceHeight := face.Metrics().Height
|
faceHeight := fc.Metrics().Height
|
||||||
|
|
||||||
for _, r := range text {
|
for _, r := range text {
|
||||||
if prevR >= 0 {
|
if prevR >= 0 {
|
||||||
dx += face.Kern(prevR, r)
|
dx += fc.Kern(prevR, r)
|
||||||
}
|
}
|
||||||
if r == '\n' {
|
if r == '\n' {
|
||||||
dx = 0
|
dx = 0
|
||||||
@ -253,17 +239,17 @@ func DrawWithOptions(dst *ebiten.Image, text string, face font.Face, options *eb
|
|||||||
|
|
||||||
// Adjust the position to the integers.
|
// Adjust the position to the integers.
|
||||||
// The current glyph images assume that they are rendered on integer positions so far.
|
// The current glyph images assume that they are rendered on integer positions so far.
|
||||||
b := getGlyphBounds(face, r)
|
b, a, _ := fc.GlyphBounds(r)
|
||||||
offset := fixed.Point26_6{
|
offset := fixed.Point26_6{
|
||||||
X: (adjustOffsetGranularity(dx) + b.Min.X) & ((1 << 6) - 1),
|
X: (adjustOffsetGranularity(dx) + b.Min.X) & ((1 << 6) - 1),
|
||||||
Y: b.Min.Y & ((1 << 6) - 1),
|
Y: b.Min.Y & ((1 << 6) - 1),
|
||||||
}
|
}
|
||||||
img := getGlyphImage(face, r, offset)
|
img := getGlyphImage(fc, r, offset)
|
||||||
drawGlyph(dst, img, fixed.Point26_6{
|
drawGlyph(dst, img, fixed.Point26_6{
|
||||||
X: dx + b.Min.X - offset.X,
|
X: dx + b.Min.X - offset.X,
|
||||||
Y: dy + b.Min.Y - offset.Y,
|
Y: dy + b.Min.Y - offset.Y,
|
||||||
}, options)
|
}, options)
|
||||||
dx += glyphAdvance(face, r)
|
dx += a
|
||||||
|
|
||||||
prevR = r
|
prevR = r
|
||||||
}
|
}
|
||||||
@ -275,11 +261,11 @@ func DrawWithOptions(dst *ebiten.Image, text string, face font.Face, options *eb
|
|||||||
const cacheSoftLimit = 512
|
const cacheSoftLimit = 512
|
||||||
|
|
||||||
// Clean up the cache.
|
// Clean up the cache.
|
||||||
if len(glyphImageCache[face]) > cacheSoftLimit {
|
if len(glyphImageCache[fc]) > cacheSoftLimit {
|
||||||
for r, e := range glyphImageCache[face] {
|
for r, e := range glyphImageCache[fc] {
|
||||||
// 60 is an arbitrary number.
|
// 60 is an arbitrary number.
|
||||||
if e.atime < now()-60 {
|
if e.atime < now()-60 {
|
||||||
delete(glyphImageCache[face], r)
|
delete(glyphImageCache[fc], r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -304,7 +290,9 @@ func BoundString(face font.Face, text string) image.Rectangle {
|
|||||||
textM.Lock()
|
textM.Lock()
|
||||||
defer textM.Unlock()
|
defer textM.Unlock()
|
||||||
|
|
||||||
m := face.Metrics()
|
fc := faceWithCacheFromFace(face)
|
||||||
|
|
||||||
|
m := fc.Metrics()
|
||||||
faceHeight := m.Height
|
faceHeight := m.Height
|
||||||
|
|
||||||
fx, fy := fixed.I(0), fixed.I(0)
|
fx, fy := fixed.I(0), fixed.I(0)
|
||||||
@ -313,7 +301,7 @@ func BoundString(face font.Face, text string) image.Rectangle {
|
|||||||
var bounds fixed.Rectangle26_6
|
var bounds fixed.Rectangle26_6
|
||||||
for _, r := range text {
|
for _, r := range text {
|
||||||
if prevR >= 0 {
|
if prevR >= 0 {
|
||||||
fx += face.Kern(prevR, r)
|
fx += fc.Kern(prevR, r)
|
||||||
}
|
}
|
||||||
if r == '\n' {
|
if r == '\n' {
|
||||||
fx = fixed.I(0)
|
fx = fixed.I(0)
|
||||||
@ -322,14 +310,14 @@ func BoundString(face font.Face, text string) image.Rectangle {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
b := getGlyphBounds(face, r)
|
b, a, _ := fc.GlyphBounds(r)
|
||||||
b.Min.X += fx
|
b.Min.X += fx
|
||||||
b.Max.X += fx
|
b.Max.X += fx
|
||||||
b.Min.Y += fy
|
b.Min.Y += fy
|
||||||
b.Max.Y += fy
|
b.Max.Y += fy
|
||||||
bounds = bounds.Union(b)
|
bounds = bounds.Union(b)
|
||||||
|
|
||||||
fx += glyphAdvance(face, r)
|
fx += a
|
||||||
prevR = r
|
prevR = r
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,17 +357,19 @@ func CacheGlyphs(face font.Face, text string) {
|
|||||||
textM.Lock()
|
textM.Lock()
|
||||||
defer textM.Unlock()
|
defer textM.Unlock()
|
||||||
|
|
||||||
|
fc := faceWithCacheFromFace(face)
|
||||||
|
|
||||||
var dx fixed.Int26_6
|
var dx fixed.Int26_6
|
||||||
prevR := rune(-1)
|
prevR := rune(-1)
|
||||||
for _, r := range text {
|
for _, r := range text {
|
||||||
if prevR >= 0 {
|
if prevR >= 0 {
|
||||||
dx += face.Kern(prevR, r)
|
dx += fc.Kern(prevR, r)
|
||||||
}
|
}
|
||||||
if r == '\n' {
|
if r == '\n' {
|
||||||
dx = 0
|
dx = 0
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
b := getGlyphBounds(face, r)
|
b, a, _ := fc.GlyphBounds(r)
|
||||||
|
|
||||||
// Cache all 4 variations for one rune (#2528).
|
// Cache all 4 variations for one rune (#2528).
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
@ -387,10 +377,10 @@ func CacheGlyphs(face font.Face, text string) {
|
|||||||
X: (fixed.Int26_6(i*(1<<4)) + b.Min.X) & ((1 << 6) - 1),
|
X: (fixed.Int26_6(i*(1<<4)) + b.Min.X) & ((1 << 6) - 1),
|
||||||
Y: b.Min.Y & ((1 << 6) - 1),
|
Y: b.Min.Y & ((1 << 6) - 1),
|
||||||
}
|
}
|
||||||
getGlyphImage(face, r, offset)
|
getGlyphImage(fc, r, offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
dx += glyphAdvance(face, r)
|
dx += a
|
||||||
prevR = r
|
prevR = r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -462,14 +452,16 @@ func AppendGlyphs(glyphs []Glyph, face font.Face, text string) []Glyph {
|
|||||||
textM.Lock()
|
textM.Lock()
|
||||||
defer textM.Unlock()
|
defer textM.Unlock()
|
||||||
|
|
||||||
|
fc := faceWithCacheFromFace(face)
|
||||||
|
|
||||||
var pos fixed.Point26_6
|
var pos fixed.Point26_6
|
||||||
prevR := rune(-1)
|
prevR := rune(-1)
|
||||||
|
|
||||||
faceHeight := face.Metrics().Height
|
faceHeight := fc.Metrics().Height
|
||||||
|
|
||||||
for _, r := range text {
|
for _, r := range text {
|
||||||
if prevR >= 0 {
|
if prevR >= 0 {
|
||||||
pos.X += face.Kern(prevR, r)
|
pos.X += fc.Kern(prevR, r)
|
||||||
}
|
}
|
||||||
if r == '\n' {
|
if r == '\n' {
|
||||||
pos.X = 0
|
pos.X = 0
|
||||||
@ -478,12 +470,12 @@ func AppendGlyphs(glyphs []Glyph, face font.Face, text string) []Glyph {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
b := getGlyphBounds(face, r)
|
b, a, _ := fc.GlyphBounds(r)
|
||||||
offset := fixed.Point26_6{
|
offset := fixed.Point26_6{
|
||||||
X: (adjustOffsetGranularity(pos.X) + b.Min.X) & ((1 << 6) - 1),
|
X: (adjustOffsetGranularity(pos.X) + b.Min.X) & ((1 << 6) - 1),
|
||||||
Y: b.Min.Y & ((1 << 6) - 1),
|
Y: b.Min.Y & ((1 << 6) - 1),
|
||||||
}
|
}
|
||||||
if img := getGlyphImage(face, r, offset); img != nil {
|
if img := getGlyphImage(fc, r, offset); img != nil {
|
||||||
// Adjust the position to the integers.
|
// Adjust the position to the integers.
|
||||||
// The current glyph images assume that they are rendered on integer positions so far.
|
// The current glyph images assume that they are rendered on integer positions so far.
|
||||||
glyphs = append(glyphs, Glyph{
|
glyphs = append(glyphs, Glyph{
|
||||||
@ -493,7 +485,7 @@ func AppendGlyphs(glyphs []Glyph, face font.Face, text string) []Glyph {
|
|||||||
Y: fixed26_6ToFloat64(pos.Y + b.Min.Y - offset.Y),
|
Y: fixed26_6ToFloat64(pos.Y + b.Min.Y - offset.Y),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pos.X += glyphAdvance(face, r)
|
pos.X += a
|
||||||
prevR = r
|
prevR = r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user