generated from macoy/gamelib-project-template
2 changed files with 140 additions and 131 deletions
@ -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)) |
Loading…
Reference in new issue