Skip to content

Commit cb4c373

Browse files
committed
SDL2 TTF font renderering, fist try.
1 parent 1e3fa7a commit cb4c373

File tree

5 files changed

+330
-0
lines changed

5 files changed

+330
-0
lines changed

README.org

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ Advertise your module here, open a PR and include a org-mode link!
129129
- [[./util/productivity/README.org][productivity]] :: Lock StumpWM down so you have to get work done.
130130
- [[./util/qubes/README.org][qubes]] :: Integration to Qubes OS (https://www.qubes-os.org)
131131
- [[./util/screenshot/README.org][screenshot]] :: Takes screenshots and stores them as png files
132+
- [[./util/sdl2-fonts/README.md ./util/sdl2-fonts/README.txt][sdl2-fonts]] :: SDL2-based TTF font rendering for StumpWM.
132133
- [[./util/searchengines/README.org][searchengines]] :: Allows searching text using prompt or clipboard contents with various search engines
133134
- [[./util/shell-command-history/README.org][shell-command-history]] :: Save and load the stumpwm::*input-shell-history* to a file
134135
- [[./util/spatial-groups/README][spatial-groups]] :: Spatial Groups navigation for StumpWM

util/sdl-fonts/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
A TTF renderer for StumpWM which uses SDL. SDL is actively developed by the
2+
whole world so the TTF code is hopefully good as well.
3+
4+
This module requires the native packages SDL2, SDL2_ttf and libffi.
5+
Make sure they are installed.
6+
7+
Load a font through by
8+
9+
```
10+
(defparameter *the-font* (sdl-fonts:load-font "/usr/share/fonts/TTF/DroidSansMono.ttf" 14))
11+
```
12+
13+
And use it through
14+
15+
```
16+
(set-font *the-font*)
17+
```
18+
19+
Of course you can also use multiple fonts. If you want you can also set the
20+
hinting through
21+
22+
```
23+
(defparameter *the-font* (sdl-fonts:load-font "/usr/share/fonts/TTF/DroidSansMono.ttf" 14 :hinting <hinting>))
24+
```
25+
26+
where `<hinting>` is one of `:normal`, `:light`, `:mono`, `:none`, `:light-subpixel`.
27+
28+
Have fun!
29+
30+
Use at your own risk and discretion. I assume no responsibility for any damage
31+
resulting from using this project. I do use it myself.
32+
33+
This modules borrows ideas and code from clx-truetype.

util/sdl-fonts/package.lisp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
;;;; package.lisp
2+
3+
(defpackage #:sdl-fonts
4+
(:use #:cl))
5+
6+
(in-package #:sdl-fonts)
7+
8+
(import '(stumpwm::font-exists-p
9+
stumpwm::open-font
10+
stumpwm::close-font
11+
stumpwm::font-ascent
12+
stumpwm::font-descent
13+
stumpwm::text-line-width
14+
stumpwm::draw-image-glyphs
15+
stumpwm::font-height
16+
cffi:define-foreign-library
17+
cffi:use-foreign-library
18+
cffi:defcstruct
19+
cffi:defcfun
20+
cffi:foreign-slot-value
21+
cffi:with-foreign-object
22+
cffi:foreign-type-size
23+
cffi:mem-ref
24+
cffi:inc-pointer))

util/sdl-fonts/sdl-fonts.asd

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
;;;; sdl-fonts.asd
2+
3+
(asdf:defsystem #:sdl-fonts
4+
:serial t
5+
:description "SDL-based TTF font rendering for StumpWM."
6+
:version "1.0.0"
7+
:author "Mihail Ivanchev"
8+
:license "MIT"
9+
:depends-on (#:stumpwm #:cffi #:cffi-libffi)
10+
:components ((:file "package")
11+
(:file "sdl-fonts")))
12+

util/sdl-fonts/sdl-fonts.lisp

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
;;;; sdl-fonts.lisp
2+
3+
(in-package #:sdl-fonts)
4+
5+
(export '(load-font
6+
font-exists-p
7+
open-font
8+
close-font
9+
font-ascent
10+
font-descent
11+
font-height
12+
text-line-width
13+
draw-image-glyphs))
14+
15+
(define-foreign-library libsdl2
16+
(:unix (:or "libSDL2-2.0.so.0" "libSDL2.so.0.2" "libSDL2")))
17+
18+
(define-foreign-library libsdl2-ttf
19+
(:unix (:or "libSDL2_ttf-2.0.so.0" "libSDL2_ttf")))
20+
21+
(use-foreign-library libsdl2)
22+
(use-foreign-library libsdl2-ttf)
23+
24+
(defcstruct sdl-rect
25+
(x :int)
26+
(y :int)
27+
(w :int)
28+
(h :int))
29+
30+
(defcstruct sdl-surface
31+
(flags :uint32)
32+
(format :pointer)
33+
(w :int)
34+
(h :int)
35+
(pitch :int)
36+
(pixels :pointer)
37+
(userdata :pointer)
38+
(locked :int)
39+
(list-bitmap :pointer)
40+
(clip-rect (:struct sdl-rect))
41+
(map :pointer)
42+
(refcount :int))
43+
44+
(defcstruct sdl-color
45+
(r :uint8)
46+
(g :uint8)
47+
(b :uint8)
48+
(a :uint8))
49+
50+
(defvar *hinting-flags* '(:normal :light :mono :light-subpixel))
51+
52+
(defcfun "SDL_Init" :int (flags :long))
53+
(defcfun "SDL_WasInit" :int (flags :long))
54+
(defcfun "SDL_LockSurface" :void (surf :pointer))
55+
(defcfun "SDL_UnlockSurface" :void (surf :pointer))
56+
(defcfun "SDL_FreeSurface" :void (surf :pointer))
57+
(defcfun "TTF_Init" :int)
58+
(defcfun "TTF_WasInit" :int)
59+
(defcfun "TTF_OpenFont" :pointer (file :string) (ptsize :int))
60+
(defcfun "TTF_SizeUTF8" :int (font :pointer)
61+
(text :string)
62+
(w :pointer)
63+
(h :pointer))
64+
(defcfun "TTF_RenderUTF8_Blended" :pointer
65+
(font :pointer)
66+
(text :string)
67+
(fg (:struct sdl-color)))
68+
(defcfun "TTF_FontAscent" :int (font :pointer))
69+
(defcfun "TTF_FontDescent" :int (font :pointer))
70+
(defcfun "TTF_FontHeight" :int (font :pointer))
71+
(defcfun "TTF_SetFontSizeDPI" :int (font :pointer)
72+
(ptsize :int)
73+
(hdpi :unsigned-int)
74+
(vdpi :unsigned-int))
75+
(defcfun "TTF_SetFontHinting" :int (font :pointer) (hinting :int))
76+
77+
(defclass font ()
78+
((sdl2-ptr
79+
:initarg :sdl2-ptr
80+
:accessor sdl2-ptr)
81+
(size
82+
:initarg :size
83+
:accessor font-size)
84+
(hdpi
85+
:initform nil
86+
:accessor font-hdpi)
87+
(vdpi
88+
:initform nil
89+
:accessor font-vdpi)))
90+
91+
(defconstant SDL_INIT_VIDEO #x00000020)
92+
93+
(defun load-font (path size &key hinting)
94+
(assert (or (not hinting) (member hinting *hinting-flags*)))
95+
(when (zerop (sdl-wasinit SDL_INIT_VIDEO))
96+
(sdl-init SDL_INIT_VIDEO))
97+
(when (zerop (ttf-wasinit))
98+
(ttf-init))
99+
(let ((font (make-instance 'font
100+
:sdl2-ptr (ttf-openfont path size)
101+
:size size)))
102+
(when hinting
103+
(ttf-setfonthinting (sdl2-ptr font)
104+
(position hinting *hinting-flags*)))
105+
font))
106+
107+
(defmethod font-exists-p ((font font))
108+
t)
109+
110+
(defmethod open-font (display (font font))
111+
font)
112+
113+
(defmethod close-font ((font font))
114+
t)
115+
116+
(defmethod font-ascent ((font font))
117+
(ttf-fontascent (sdl2-ptr font)))
118+
119+
(defmethod font-descent ((font font))
120+
(ttf-fontdescent (sdl2-ptr font)))
121+
122+
(defmethod font-height ((font font))
123+
(ttf-fontheight (sdl2-ptr font)))
124+
125+
(defmethod text-line-width ((font font) text &rest keys &key (start 0) end translate)
126+
(declare (ignorable keys start end translate))
127+
(with-foreign-object (sizes :int 2)
128+
(ttf-sizeutf8
129+
(sdl2-ptr font)
130+
text
131+
sizes
132+
(inc-pointer sizes (foreign-type-size :int)))
133+
(mem-ref sizes :int 0)))
134+
135+
(defun get-destination-picture (drawable)
136+
(or (getf (xlib:drawable-plist drawable) :ttf-surface)
137+
(setf (getf (xlib:drawable-plist drawable) :ttf-surface)
138+
(xlib:render-create-picture
139+
drawable
140+
:format (first (xlib::find-matching-picture-formats (xlib:drawable-display drawable)
141+
:depth (xlib:drawable-depth drawable)))))))
142+
(defun get-source-pixmap (drawable)
143+
(or (getf (xlib:drawable-plist drawable) :ttf-pen-surface)
144+
(setf (getf (xlib:drawable-plist drawable) :ttf-pen-surface)
145+
(xlib:create-pixmap
146+
:drawable drawable
147+
:depth (xlib:drawable-depth drawable)
148+
:width 1 :height 1))))
149+
150+
(defun get-source-picture (drawable)
151+
(or (getf (xlib:drawable-plist drawable) :ttf-pen)
152+
(setf (getf (xlib:drawable-plist drawable) :ttf-pen)
153+
(xlib:render-create-picture
154+
(get-source-pixmap drawable)
155+
:format (first (xlib::find-matching-picture-formats (xlib:drawable-display drawable)
156+
:depth (xlib:drawable-depth drawable)))
157+
:repeat :on))))
158+
159+
(defun display-alpha-picture-format (display)
160+
(or (getf (xlib:display-plist display) :ttf-alpha-format)
161+
(setf (getf (xlib:display-plist display) :ttf-alpha-format)
162+
(first
163+
(xlib:find-matching-picture-formats
164+
display
165+
:depth 8 :alpha 8 :red 0 :blue 0 :green 0)))))
166+
167+
(defun drawable-screen (drawable)
168+
(typecase drawable
169+
(xlib:drawable
170+
(dolist (screen (xlib:display-roots (xlib:drawable-display drawable)))
171+
(when (xlib:drawable-equal (xlib:screen-root screen) (xlib:drawable-root drawable))
172+
(return screen))))
173+
(xlib:screen drawable)
174+
(t nil)))
175+
176+
(defun screen-default-dpi (screen)
177+
"Returns default dpi for @var{screen}. pixel width * 25.4/millimeters width"
178+
(values (floor (* (xlib:screen-width screen) 25.4)
179+
(xlib:screen-width-in-millimeters screen))
180+
(floor (* (xlib:screen-height screen) 25.4)
181+
(xlib:screen-height-in-millimeters screen))))
182+
183+
(defun update-dpi (font drawable)
184+
(multiple-value-bind (hdpi vdpi) (screen-default-dpi (drawable-screen drawable))
185+
(when (or (not (eq (font-hdpi font) hdpi))
186+
(not (eq (font-vdpi font) vdpi)))
187+
(ttf-setfontsizedpi (sdl2-ptr font) (font-size font) hdpi vdpi)
188+
(setf (font-hdpi font) hdpi)
189+
(setf (font-vdpi font) vdpi))))
190+
191+
(defmethod draw-image-glyphs (drawable
192+
gcontext
193+
(font font)
194+
x y
195+
sequence &rest keys
196+
&key (start 0) end translate width size)
197+
(declare (ignorable keys start end translate width size))
198+
; Update the DPI in case it has changed (i.e. rendering to another screen).
199+
(update-dpi font drawable)
200+
; This is ugly code but the idea is that we don't really know anything about
201+
; the color format etc. of the thing we are drawing on (i.e. the screen). So
202+
; we use the alpha channel of the rendered glyphs to stencil out a rectangle
203+
; in the foreground color onto a rectangle of the background color. This is
204+
; the only way to properly handle all possible cases.
205+
(let* ((surf (ttf-renderutf8-blended
206+
(sdl2-ptr font)
207+
sequence
208+
'(r 0 b 0 g 0 a 0)))
209+
(width (foreign-slot-value surf '(:struct sdl-surface) 'w))
210+
(height (foreign-slot-value surf '(:struct sdl-surface) 'h))
211+
(pitch (foreign-slot-value surf '(:struct sdl-surface) 'pitch))
212+
(data (make-array (list height width) :element-type '(unsigned-byte 8))))
213+
(sdl-locksurface surf)
214+
(let ((pixels (foreign-slot-value surf '(:struct sdl-surface) 'pixels)))
215+
(dotimes (jj height)
216+
(dotimes (ii width)
217+
(let ((alpha (mem-ref pixels :uint8 (+ (* jj pitch) (* ii 4) 3))))
218+
(setf (aref data jj ii) alpha)))))
219+
(sdl-unlocksurface surf)
220+
(sdl-freesurface surf)
221+
(let* ((display (xlib:drawable-display drawable))
222+
(image (xlib:create-image
223+
:depth 8
224+
:width width
225+
:height height
226+
:data data))
227+
(alpha-pixmap (xlib:create-pixmap :width width
228+
:height height
229+
:depth 8
230+
:drawable drawable))
231+
(alpha-gc (xlib:create-gcontext :drawable alpha-pixmap))
232+
(alpha-pic
233+
(progn
234+
(xlib:put-image alpha-pixmap alpha-gc image :x 0 :y 0)
235+
(xlib:render-create-picture alpha-pixmap
236+
:format (display-alpha-picture-format display))))
237+
(src-pic (get-source-picture drawable))
238+
(dst-pic (get-destination-picture drawable)))
239+
(xlib:free-gcontext alpha-gc)
240+
; Paint the source & destination surfaces in the foreground and
241+
; background colors of the context.
242+
(xlib:draw-point (get-source-pixmap drawable) gcontext 0 0)
243+
(let ((fg (xlib:gcontext-foreground gcontext)))
244+
(setf (xlib:gcontext-foreground gcontext) (xlib:gcontext-background gcontext))
245+
(xlib:draw-rectangle drawable gcontext x (- y (font-ascent font)) width height t)
246+
(setf (xlib:gcontext-foreground gcontext) fg))
247+
(xlib:render-composite :over
248+
src-pic
249+
alpha-pic
250+
dst-pic
251+
0
252+
0
253+
0
254+
0
255+
x
256+
(- y (font-ascent font))
257+
width
258+
height)
259+
(xlib:render-free-picture alpha-pic)
260+
(xlib:free-pixmap alpha-pixmap))))

0 commit comments

Comments
 (0)