|
| 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