text: replace Family with Shaper, add Font, Face

There is now a single shaping implementation, Shaper, for all fonts, replacing
Family that only covered a single typeface.

A typeface is identified by a name, where the empty string denotes the
default typeface.

Font is introduced to specify a particular font from the typeface, style,
weight and size.

Face is changed to an interface for a particular layout and shaping method.
The text/shape package is renamed to text/opentype and contains a Face
implementation based on golang.org/x/image/font/sfnt.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2019-10-08 21:29:04 +02:00
parent ea404bc8fc
commit bef7c39e4c
8 changed files with 233 additions and 198 deletions
+121
View File
@@ -0,0 +1,121 @@
// SPDX-License-Identifier: Unlicense OR MIT
package text
import (
"gioui.org/op/paint"
"golang.org/x/image/math/fixed"
)
type layoutCache struct {
m map[layoutKey]*layoutElem
head, tail *layoutElem
}
type pathCache struct {
m map[pathKey]*path
head, tail *path
}
type layoutElem struct {
next, prev *layoutElem
key layoutKey
layout *Layout
}
type path struct {
next, prev *path
key pathKey
val paint.ClipOp
}
type layoutKey struct {
ppem fixed.Int26_6
str string
opts LayoutOptions
}
type pathKey struct {
ppem fixed.Int26_6
str string
}
const maxSize = 1000
func (l *layoutCache) Get(k layoutKey) (*Layout, bool) {
if lt, ok := l.m[k]; ok {
l.remove(lt)
l.insert(lt)
return lt.layout, true
}
return nil, false
}
func (l *layoutCache) Put(k layoutKey, lt *Layout) {
if l.m == nil {
l.m = make(map[layoutKey]*layoutElem)
l.head = new(layoutElem)
l.tail = new(layoutElem)
l.head.prev = l.tail
l.tail.next = l.head
}
val := &layoutElem{key: k, layout: lt}
l.m[k] = val
l.insert(val)
if len(l.m) > maxSize {
oldest := l.tail.next
l.remove(oldest)
delete(l.m, oldest.key)
}
}
func (l *layoutCache) remove(lt *layoutElem) {
lt.next.prev = lt.prev
lt.prev.next = lt.next
}
func (l *layoutCache) insert(lt *layoutElem) {
lt.next = l.head
lt.prev = l.head.prev
lt.prev.next = lt
lt.next.prev = lt
}
func (c *pathCache) Get(k pathKey) (paint.ClipOp, bool) {
if v, ok := c.m[k]; ok {
c.remove(v)
c.insert(v)
return v.val, true
}
return paint.ClipOp{}, false
}
func (c *pathCache) Put(k pathKey, v paint.ClipOp) {
if c.m == nil {
c.m = make(map[pathKey]*path)
c.head = new(path)
c.tail = new(path)
c.head.prev = c.tail
c.tail.next = c.head
}
val := &path{key: k, val: v}
c.m[k] = val
c.insert(val)
if len(c.m) > maxSize {
oldest := c.tail.next
c.remove(oldest)
delete(c.m, oldest.key)
}
}
func (c *pathCache) remove(v *path) {
v.next.prev = v.prev
v.prev.next = v.next
}
func (c *pathCache) insert(v *path) {
v.next = c.head
v.prev = c.head.prev
v.prev.next = v
v.next.prev = v
}