-
Notifications
You must be signed in to change notification settings - Fork 14
/
font.lisp
130 lines (102 loc) · 5.05 KB
/
font.lisp
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
(cl:in-package :cl-bodge.text)
(defgeneric font-atlas-texture (resource))
(defgeneric font-ascender-height (resource))
(defgeneric font-descender-height (resource))
(defgeneric font-line-gap (resource))
(defgeneric find-glyph (resource character))
(defgeneric find-kerning (this-glyph that-glyph))
(defgeneric glyph-character (resource))
(defgeneric glyph-origin (resource))
(defgeneric glyph-bounding-box (resource))
(defgeneric glyph-advance-width (resource))
(defclass glyph ()
((character :initarg :character :reader glyph-character)
(origin :initarg :origin :reader glyph-origin)
(bounding-box :initarg :bounding-box :reader glyph-bounding-box)
(advance-width :initarg :advance-width :reader glyph-advance-width)
(kerning-table :initform nil)))
(defmethod initialize-instance :after ((this glyph) &key kernings)
(setf (slot-value this 'kerning-table) (alist-hash-table kernings :test 'equal)))
(defmethod find-kerning ((this glyph) (that character))
(with-slots (kerning-table) this
(gethash that kerning-table 0)))
(defmethod find-kerning ((this glyph) (that glyph))
(find-kerning this (glyph-character that)))
(defclass font (disposable)
((glyph-table :initform (make-hash-table :test 'equal))
(atlas :initarg :atlas :reader font-atlas-texture)
(ascender-height :initarg :ascender-height :reader font-ascender-height)
(descender-height :initarg :descender-height :reader font-descender-height)
(line-gap :initarg :line-gap :reader font-line-gap)))
(defmethod initialize-instance :after ((this font) &key glyphs)
(with-slots (glyph-table) this
(loop for g in glyphs
do (setf (gethash (glyph-character g) glyph-table) g))))
(define-destructor font (atlas)
(dispose atlas))
(defmethod find-glyph ((this font) character)
(with-slots (glyph-table) this
(gethash character glyph-table)))
(defun make-glyph (character origin bounding-box advance-width kernings)
(make-instance 'glyph
:character character
:origin origin
:bounding-box bounding-box
:advance-width advance-width
:kernings kernings))
(define-system-function bake-font graphics-system (atlas-image glyphs ascender-height
descender-height
line-gap)
(make-instance 'font
:glyphs glyphs
:atlas (make-2d-texture atlas-image :grey :generate-mipmaps-p nil)
:ascender-height ascender-height
:descender-height descender-height
:line-gap line-gap))
(defun walk-string (string font &optional walker)
(let* ((line-height (+ (font-ascender-height font)
(font-descender-height font)
(font-line-gap font)))
(len (length string))
(atlas (font-atlas-texture font)))
(destructuring-bind (atlas-w atlas-h) (texture-dimensions atlas)
(loop with y = 0.0 and x-max = 0.0 and idx = 0 and size = 0
for next-idx = (or (position #\Newline string :start idx) len)
for x = 0.0
for prev-g = nil
append (loop for i from idx below next-idx
for c = (aref string i)
for g = (find-glyph font c)
for (x0-box y0-box x1-box y1-box) = (glyph-bounding-box g)
for (x-orig y-orig) = (glyph-origin g)
for advance = (glyph-advance-width g)
collect (let ((kerning (if prev-g (find-kerning prev-g g) 0)))
(prog1
(when walker
(let ((x-offset (f (- x x-orig)))
(y-offset (f (- y y-orig))))
(funcall walker
x-offset
y-offset
(f (+ x-offset (- x1-box x0-box)))
(f (+ y-offset (- y1-box y0-box)))
(f (/ x0-box atlas-w))
(f (/ (- atlas-h y1-box) atlas-h))
(f (/ x1-box atlas-w))
(f (/ (- atlas-h y0-box) atlas-h)))))
(setf x (+ x kerning advance)
prev-g g)))
into line-result
finally
(when (> x x-max) (setf x-max x))
(incf y line-height)
(incf size (- next-idx idx))
(setf idx (1+ next-idx))
(return line-result))
into result
until (= next-idx len)
finally (return (values size x-max (abs y)))))))
(defun measure-string (string font)
(multiple-value-bind (size width height) (walk-string string font)
(declare (ignore size))
(list width height)))