Browse Source

Move font code into its own file

master
Macoy Madson 1 year ago
parent
commit
9ec8ca7a11
  1. 135
      src/FontAtlas.cake
  2. 136
      src/Presentation.cake

135
src/FontAtlas.cake

@ -0,0 +1,135 @@
(import
;; GameLib
"FreeType.cake" "Dictionary.cake")
(c-import "<stdio.h>"
&with-decls "<stdint.h>")
(defstruct glyph-entry
key char
x uint16_t
y uint16_t
width uint16_t
height uint16_t)
(defstruct font-atlas
glyph-lookup-table (* glyph-entry) ;; dictionary
pixel-buffer (* (unsigned char)) ;; malloc'd
width uint16_t
height uint16_t)
(defun free-font-atlas (font-atlas-to-free (* font-atlas))
(dict-free (path font-atlas-to-free > glyph-lookup-table))
(free (path font-atlas-to-free > pixel-buffer)))
;; Returns 0 for success or anything else for failure
(defun build-font-atlas (font-face (* (const (unsigned char)))
font-face-size (unsigned int)
font-atlas-out (* font-atlas) &return int)
(var freetype-library FT_Library)
(var result int
(FT_Init_FreeType (addr freetype-library)))
(freetype-check-result-or-return result "initializing FreeType")
(defer (FT_Done_FreeType freetype-library))
(var typeface FT_Face)
(var face-index int 0)
(set result (FT_New_Memory_Face freetype-library font-face font-face-size
face-index (addr typeface)))
(unless (= result FT_Err_Ok)
(fprintf stderr "error: encountered error %d while loading font\n" result)
(return 1))
(defer (FT_Done_Face typeface))
(set result (FT_Set_Char_Size
typeface ;; handle to face object
0 ;; char_width in 1/64th of points
(* 16 64) ;; char_height in 1/64th of points
300 ;; horizontal device resolution
300)) ;; vertical device resolution
(freetype-check-result-or-return result "setting character size")
(defstruct ascii-glyph-range
start-character char
end-character-inclusive char)
;; This is a bit silly
(var ranges-to-render ([] ascii-glyph-range)
(array (array 'a' 'z') (array 'A' 'Z') (array '0' '9') (array '!' '/') (array ':' '@')
(array '[' '`') (array '{' '~')))
(var atlas-width uint16_t 512)
(var atlas-height uint16_t 512)
(var num-components-per-pixel char 4)
(set (path font-atlas-out > pixel-buffer)
(type-cast (malloc (* atlas-width atlas-height num-components-per-pixel))
(* (unsigned char))))
(set (path font-atlas-out > width) atlas-width)
(set (path font-atlas-out > height) atlas-height)
(var atlas-write-x uint16_t 0)
(var atlas-write-y uint16_t 0)
(var atlas-tallest-character-this-row uint16_t 0)
(each-in-array ranges-to-render range-index
(var range (* ascii-glyph-range) (addr (at range-index ranges-to-render)))
(each-in-range (- (+ 1 (path range > end-character-inclusive)) (path range > start-character))
character-offset-from-start
(var character char (+ character-offset-from-start (path range > start-character)))
(var glyph-index int (FT_Get_Char_Index typeface character))
(unless glyph-index
(fprintf stderr "error: could not find glyph '%c' in font\n" character)
(return 1))
(set result (FT_Load_Glyph typeface glyph-index FT_LOAD_DEFAULT))
(freetype-check-result-or-return result "loading glyph")
;; One slot for the whole face
(set result (FT_Render_Glyph (path typeface > glyph) FT_RENDER_MODE_NORMAL))
(freetype-check-result-or-return result "rendering glyph")
(var num-rows int (path typeface > glyph > bitmap . rows))
(var num-columns int (path typeface > glyph > bitmap . width))
(when (> num-rows atlas-tallest-character-this-row)
(set atlas-tallest-character-this-row num-rows))
(when (> (+ num-columns atlas-write-x) atlas-width)
(set atlas-write-x 0)
(set atlas-write-y (+ atlas-write-y atlas-tallest-character-this-row))
(set atlas-tallest-character-this-row num-rows)
(when (> (+ atlas-write-y atlas-tallest-character-this-row) atlas-height)
(fprintf stderr "error: ran out of space in atlas size %dx%d\n" atlas-width atlas-height)
(return 1)))
(var new-glyph-entry glyph-entry (array 0))
(set (field new-glyph-entry key) 'g')
(set (field new-glyph-entry x) atlas-write-x)
(set (field new-glyph-entry y) atlas-write-y)
(set (field new-glyph-entry width) num-columns)
(set (field new-glyph-entry height) num-rows)
(dict-set-struct (path font-atlas-out > glyph-lookup-table) new-glyph-entry)
;; TODO: This can become a memcpy if we can get FreeType to render to our same format
(each-in-range num-rows row
(each-in-range num-columns column
(var current-pixel (* (unsigned char))
(addr
(at (+ column (* row num-columns))
(path typeface > glyph > bitmap . buffer))))
(var current-pixel-out (* (unsigned char))
(addr
(at (* num-components-per-pixel
(+ (+ column atlas-write-x) (* (+ atlas-write-y row) atlas-width)))
(path font-atlas-out > pixel-buffer))))
(if (> (deref current-pixel) 128)
(scope
(each-in-range 3 component
(set (at component current-pixel-out) 255))
(set (at 3 current-pixel-out) 0))
;; Full alpha for empty pixels
(set (at 3 current-pixel-out) 255))))
;; Wrapping happens after we know what the next glyph's dimensions are
(set atlas-write-x (+ atlas-write-x num-columns))))
(return 0))

136
src/Presentation.cake

@ -3,9 +3,9 @@
(add-cakelisp-search-directory "Dependencies/cakelisp/runtime")
(add-cakelisp-search-directory "src")
(import
(import "FontAtlas.cake"
;; GameLib
"SDL.cake" "FreeType.cake" "DynamicArray.cake" "Dictionary.cake" "DataBundle.cake")
"SDL.cake" "DynamicArray.cake" "Dictionary.cake" "DataBundle.cake")
(c-import "<stdio.h>"
&with-decls "<stdint.h>")
@ -27,134 +27,6 @@
(fprintf stderr (token-splice format)))))
(return true))
(defstruct glyph-entry
key char
x uint16_t
y uint16_t
width uint16_t
height uint16_t)
(defstruct font-atlas
glyph-lookup-table (* glyph-entry) ;; dictionary
pixel-buffer (* (unsigned char)) ;; malloc'd
width uint16_t
height uint16_t)
(defun-local free-font-atlas (font-atlas-to-free (* font-atlas))
(dict-free (path font-atlas-to-free > glyph-lookup-table))
(free (path font-atlas-to-free > pixel-buffer)))
;; Returns 0 for success or anything else for failure
(defun-local build-font-atlas (font-atlas-out (* font-atlas) &return int)
(var freetype-library FT_Library)
(var result int
(FT_Init_FreeType (addr freetype-library)))
(freetype-check-result-or-return result "initializing FreeType")
(defer (FT_Done_FreeType freetype-library))
(var typeface FT_Face)
(var face-index int 0)
(set result (FT_New_Memory_Face freetype-library s-start-ubuntu-regular-font
(- s-end-ubuntu-regular-font s-start-ubuntu-regular-font)
face-index (addr typeface)))
(unless (= result FT_Err_Ok)
(fprintf stderr "error: encountered error %d while loading font\n" result)
(return 1))
(defer (FT_Done_Face typeface))
(set result (FT_Set_Char_Size
typeface ;; handle to face object
0 ;; char_width in 1/64th of points
(* 16 64) ;; char_height in 1/64th of points
300 ;; horizontal device resolution
300)) ;; vertical device resolution
(freetype-check-result-or-return result "setting character size")
(defstruct ascii-glyph-range
start-character char
end-character-inclusive char)
;; This is a bit silly
(var ranges-to-render ([] ascii-glyph-range)
(array (array 'a' 'z') (array 'A' 'Z') (array '0' '9') (array '!' '/') (array ':' '@')
(array '[' '`') (array '{' '~')))
(var atlas-width uint16_t 512)
(var atlas-height uint16_t 512)
(var num-components-per-pixel char 4)
(set (path font-atlas-out > pixel-buffer)
(type-cast (malloc (* atlas-width atlas-height num-components-per-pixel))
(* (unsigned char))))
(set (path font-atlas-out > width) atlas-width)
(set (path font-atlas-out > height) atlas-height)
(var atlas-write-x uint16_t 0)
(var atlas-write-y uint16_t 0)
(var atlas-tallest-character-this-row uint16_t 0)
(each-in-array ranges-to-render range-index
(var range (* ascii-glyph-range) (addr (at range-index ranges-to-render)))
(each-in-range (- (+ 1 (path range > end-character-inclusive)) (path range > start-character))
character-offset-from-start
(var character char (+ character-offset-from-start (path range > start-character)))
(var glyph-index int (FT_Get_Char_Index typeface character))
(unless glyph-index
(fprintf stderr "error: could not find glyph '%c' in font\n" character)
(return 1))
(set result (FT_Load_Glyph typeface glyph-index FT_LOAD_DEFAULT))
(freetype-check-result-or-return result "loading glyph")
;; One slot for the whole face
(set result (FT_Render_Glyph (path typeface > glyph) FT_RENDER_MODE_NORMAL))
(freetype-check-result-or-return result "rendering glyph")
(var num-rows int (path typeface > glyph > bitmap . rows))
(var num-columns int (path typeface > glyph > bitmap . width))
(when (> num-rows atlas-tallest-character-this-row)
(set atlas-tallest-character-this-row num-rows))
(when (> (+ num-columns atlas-write-x) atlas-width)
(set atlas-write-x 0)
(set atlas-write-y (+ atlas-write-y atlas-tallest-character-this-row))
(set atlas-tallest-character-this-row num-rows)
(when (> (+ atlas-write-y atlas-tallest-character-this-row) atlas-height)
(fprintf stderr "error: ran out of space in atlas size %dx%d\n" atlas-width atlas-height)
(return 1)))
(var new-glyph-entry glyph-entry (array 0))
(set (field new-glyph-entry key) 'g')
(set (field new-glyph-entry x) atlas-write-x)
(set (field new-glyph-entry y) atlas-write-y)
(set (field new-glyph-entry width) num-columns)
(set (field new-glyph-entry height) num-rows)
(dict-set-struct (path font-atlas-out > glyph-lookup-table) new-glyph-entry)
;; TODO: This can become a memcpy if we can get FreeType to render to our same format
(each-in-range num-rows row
(each-in-range num-columns column
(var current-pixel (* (unsigned char))
(addr
(at (+ column (* row num-columns))
(path typeface > glyph > bitmap . buffer))))
(var current-pixel-out (* (unsigned char))
(addr
(at (* num-components-per-pixel
(+ (+ column atlas-write-x) (* (+ atlas-write-y row) atlas-width)))
(path font-atlas-out > pixel-buffer))))
(if (> (deref current-pixel) 128)
(scope
(each-in-range 3 component
(set (at component current-pixel-out) 255))
(set (at 3 current-pixel-out) 0))
;; Full alpha for empty pixels
(set (at 3 current-pixel-out) 255))))
;; Wrapping happens after we know what the next glyph's dimensions are
(set atlas-write-x (+ atlas-write-x num-columns))))
(return 0))
(defun main (&return int)
(data-bundle-load-all-resources)
@ -175,7 +47,9 @@
(defer (SDL_DestroyRenderer renderer))
(var ubuntu-regular-font-atlas font-atlas (array 0))
(unless (= 0 (build-font-atlas (addr ubuntu-regular-font-atlas)))
(unless (= 0 (build-font-atlas s-start-ubuntu-regular-font
(- s-end-ubuntu-regular-font s-start-ubuntu-regular-font)
(addr ubuntu-regular-font-atlas)))
(return 1))
(defer (free-font-atlas (addr ubuntu-regular-font-atlas)))
(var ubuntu-regular-texture (* SDL_Texture) null)

Loading…
Cancel
Save