~eliasnaur/gio

ref: 16d2a3ac0a07493f4f3f43beb6b00002a1869f44 gio/text/shaper.go -rw-r--r-- 2.9 KiB
16d2a3acElias Naur text: remove String, Layout and add Glyph 9 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// SPDX-License-Identifier: Unlicense OR MIT

package text

import (
	"golang.org/x/image/font"

	"gioui.org/op"
	"gioui.org/unit"
	"golang.org/x/image/math/fixed"
)

// Shaper implements layout and shaping of text.
type Shaper interface {
	// Layout a text according to a set of options.
	Layout(c unit.Converter, font Font, str string, opts LayoutOptions) []Line
	// Shape a line of text previously laid out by Layout.
	Shape(c unit.Converter, font Font, str string, layout []Glyph) op.CallOp
	Metrics(c unit.Converter, font Font) font.Metrics
}

// FontRegistry implements layout and shaping of text and a cache of
// computed results.
//
// If a font matches no registered shape, FontRegistry falls back to the
// first registered face.
type FontRegistry struct {
	def   Typeface
	faces map[Font]*face
}

type face struct {
	face        Face
	layoutCache layoutCache
	pathCache   pathCache
}

func (s *FontRegistry) Register(font Font, tf Face) {
	if s.faces == nil {
		s.def = font.Typeface
		s.faces = make(map[Font]*face)
	}
	// Treat all font sizes equally.
	font.Size = unit.Value{}
	if font.Weight == 0 {
		font.Weight = Normal
	}
	s.faces[font] = &face{
		face: tf,
	}
}

func (s *FontRegistry) Layout(c unit.Converter, font Font, str string, opts LayoutOptions) []Line {
	tf := s.faceForFont(font)
	return tf.layout(fixed.I(c.Px(font.Size)), str, opts)
}

func (s *FontRegistry) Shape(c unit.Converter, font Font, str string, layout []Glyph) op.CallOp {
	tf := s.faceForFont(font)
	return tf.shape(fixed.I(c.Px(font.Size)), str, layout)
}

func (s *FontRegistry) Metrics(c unit.Converter, font Font) font.Metrics {
	tf := s.faceForFont(font)
	return tf.metrics(fixed.I(c.Px(font.Size)))
}

func (s *FontRegistry) faceForStyle(font Font) *face {
	tf := s.faces[font]
	if tf == nil {
		font := font
		font.Weight = Normal
		tf = s.faces[font]
	}
	if tf == nil {
		font := font
		font.Style = Regular
		tf = s.faces[font]
	}
	if tf == nil {
		font := font
		font.Style = Regular
		font.Weight = Normal
		tf = s.faces[font]
	}
	return tf
}

func (s *FontRegistry) faceForFont(font Font) *face {
	font.Size = unit.Value{}
	tf := s.faceForStyle(font)
	if tf == nil {
		font.Typeface = s.def
		tf = s.faceForStyle(font)
	}
	return tf
}

func (t *face) layout(ppem fixed.Int26_6, str string, opts LayoutOptions) []Line {
	if t == nil {
		return nil
	}
	lk := layoutKey{
		ppem: ppem,
		str:  str,
		opts: opts,
	}
	if l, ok := t.layoutCache.Get(lk); ok {
		return l
	}
	l := t.face.Layout(ppem, str, opts)
	t.layoutCache.Put(lk, l)
	return l
}

func (t *face) shape(ppem fixed.Int26_6, str string, layout []Glyph) op.CallOp {
	if t == nil {
		return op.CallOp{}
	}
	pk := pathKey{
		ppem: ppem,
		str:  str,
	}
	if clip, ok := t.pathCache.Get(pk); ok {
		return clip
	}
	clip := t.face.Shape(ppem, layout)
	t.pathCache.Put(pk, clip)
	return clip
}

func (t *face) metrics(ppem fixed.Int26_6) font.Metrics {
	return t.face.Metrics(ppem)
}