123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- // SPDX-License-Identifier: Unlicense OR MIT
- package text
- import (
- "image"
- "sync/atomic"
- giofont "gioui.org/font"
- "gioui.org/io/system"
- "gioui.org/op"
- "gioui.org/op/clip"
- "gioui.org/op/paint"
- "golang.org/x/image/math/fixed"
- )
- // entry holds a single key-value pair for an LRU cache.
- type entry[K comparable, V any] struct {
- next, prev *entry[K, V]
- key K
- v V
- }
- // lru is a generic least-recently-used cache.
- type lru[K comparable, V any] struct {
- m map[K]*entry[K, V]
- head, tail *entry[K, V]
- }
- // Get fetches the value associated with the given key, if any.
- func (l *lru[K, V]) Get(k K) (V, bool) {
- if lt, ok := l.m[k]; ok {
- l.remove(lt)
- l.insert(lt)
- return lt.v, true
- }
- var v V
- return v, false
- }
- // Put inserts the given value with the given key, evicting old
- // cache entries if necessary.
- func (l *lru[K, V]) Put(k K, v V) {
- if l.m == nil {
- l.m = make(map[K]*entry[K, V])
- l.head = new(entry[K, V])
- l.tail = new(entry[K, V])
- l.head.prev = l.tail
- l.tail.next = l.head
- }
- val := &entry[K, V]{key: k, v: v}
- l.m[k] = val
- l.insert(val)
- if len(l.m) > maxSize {
- oldest := l.tail.next
- l.remove(oldest)
- delete(l.m, oldest.key)
- }
- }
- // remove cuts e out of the lru linked list.
- func (l *lru[K, V]) remove(e *entry[K, V]) {
- e.next.prev = e.prev
- e.prev.next = e.next
- }
- // insert adds e to the lru linked list.
- func (l *lru[K, V]) insert(e *entry[K, V]) {
- e.next = l.head
- e.prev = l.head.prev
- e.prev.next = e
- e.next.prev = e
- }
- type bitmapCache = lru[GlyphID, bitmap]
- type bitmap struct {
- img paint.ImageOp
- size image.Point
- }
- type layoutCache = lru[layoutKey, document]
- type glyphValue[V any] struct {
- v V
- glyphs []glyphInfo
- }
- type glyphLRU[V any] struct {
- seed uint64
- cache lru[uint64, glyphValue[V]]
- }
- var seed uint32
- // hashGlyphs computes a hash key based on the ID and X offset of
- // every glyph in the slice.
- func (c *glyphLRU[V]) hashGlyphs(gs []Glyph) uint64 {
- if c.seed == 0 {
- c.seed = uint64(atomic.AddUint32(&seed, 3900798947))
- }
- if len(gs) == 0 {
- return 0
- }
- h := c.seed
- firstX := gs[0].X
- for _, g := range gs {
- h += uint64(g.X - firstX)
- h *= 6585573582091643
- h += uint64(g.ID)
- h *= 3650802748644053
- }
- return h
- }
- func (c *glyphLRU[V]) Get(key uint64, gs []Glyph) (V, bool) {
- if v, ok := c.cache.Get(key); ok && gidsEqual(v.glyphs, gs) {
- return v.v, true
- }
- var v V
- return v, false
- }
- func (c *glyphLRU[V]) Put(key uint64, glyphs []Glyph, v V) {
- gids := make([]glyphInfo, len(glyphs))
- firstX := fixed.I(0)
- for i, glyph := range glyphs {
- if i == 0 {
- firstX = glyph.X
- }
- // Cache glyph X offsets relative to the first glyph.
- gids[i] = glyphInfo{ID: glyph.ID, X: glyph.X - firstX}
- }
- val := glyphValue[V]{
- glyphs: gids,
- v: v,
- }
- c.cache.Put(key, val)
- }
- type pathCache = glyphLRU[clip.PathSpec]
- type bitmapShapeCache = glyphLRU[op.CallOp]
- type glyphInfo struct {
- ID GlyphID
- X fixed.Int26_6
- }
- type layoutKey struct {
- ppem fixed.Int26_6
- maxWidth, minWidth int
- maxLines int
- str string
- truncator string
- locale system.Locale
- font giofont.Font
- forceTruncate bool
- wrapPolicy WrapPolicy
- lineHeight fixed.Int26_6
- lineHeightScale float32
- }
- const maxSize = 1000
- func gidsEqual(a []glyphInfo, glyphs []Glyph) bool {
- if len(a) != len(glyphs) {
- return false
- }
- firstX := fixed.Int26_6(0)
- for i := range a {
- if i == 0 {
- firstX = glyphs[i].X
- }
- // Cache glyph X offsets relative to the first glyph.
- if a[i].ID != glyphs[i].ID || a[i].X != (glyphs[i].X-firstX) {
- return false
- }
- }
- return true
- }
|