Browse Source

WIP Rendering of font atlas

master
Macoy Madson 1 year ago
parent
commit
ad31ea5e01
  1. 3
      .gitmodules
  2. 1
      Dependencies/FreeType
  3. 2
      Dependencies/gamelib
  4. 176
      src/Presentation.cake

3
.gitmodules

@ -10,3 +10,6 @@
[submodule "Dependencies/stb"]
path = Dependencies/stb
url = https://macoy.me/code/macoy/stb
[submodule "Dependencies/FreeType"]
path = Dependencies/FreeType
url = https://gitlab.freedesktop.org/freetype/freetype.git

1
Dependencies/FreeType

@ -0,0 +1 @@
Subproject commit dd91f6e7f5a051818070c49715125fb72074023e

2
Dependencies/gamelib

@ -1 +1 @@
Subproject commit 27180123011a633809f75e66e3d1949c6d0f00bd
Subproject commit dbf564c66633911b218bbe403b04ef99b8bf09cd

176
src/Presentation.cake

@ -5,7 +5,13 @@
(import
;; GameLib
"SDL.cake" "DynamicArray.cake")
"SDL.cake" "FreeType.cake" "DynamicArray.cake" "Dictionary.cake" "DataBundle.cake")
(c-import "<stdio.h>"
&with-decls "<stdint.h>")
(bundle-file s-start-ubuntu-regular-font s-end-ubuntu-regular-font
(unsigned char) "data/Fonts/Ubuntu-R.ttf")
(define-keybind s-quit-keybind (array SDL_SCANCODE_Q keybind-modifier-flags-ctrl))
@ -21,7 +27,137 @@
(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)
(SDL_GL_SetAttribute SDL_GL_CONTEXT_MAJOR_VERSION 4)
(SDL_GL_SetAttribute SDL_GL_CONTEXT_MINOR_VERSION 6)
(SDL_SetHint SDL_HINT_RENDER_VSYNC "1")
@ -38,6 +174,26 @@
(return 1))
(defer (SDL_DestroyRenderer renderer))
(var ubuntu-regular-font-atlas font-atlas (array 0))
(unless (= 0 (build-font-atlas (addr ubuntu-regular-font-atlas)))
(return 1))
(defer (free-font-atlas (addr ubuntu-regular-font-atlas)))
(var ubuntu-regular-texture (* SDL_Texture) null)
(defer (when ubuntu-regular-texture (SDL_DestroyTexture ubuntu-regular-texture)))
(scope
(var font-surface (* SDL_Surface)
(SDL_CreateRGBSurfaceFrom (field ubuntu-regular-font-atlas pixel-buffer)
(field ubuntu-regular-font-atlas width)
(field ubuntu-regular-font-atlas height)
32 ;; bit depth of each pixel (RGBA)
(* (field ubuntu-regular-font-atlas width) 4) ;; pitch (width of a row in bytes)
0xff000000 0x00ff0000 0x0000ff00 0x000000ff))
(defer (SDL_FreeSurface font-surface)) ;; We don't need this after making the texture
(set ubuntu-regular-texture (SDL_CreateTextureFromSurface renderer font-surface))
(unless ubuntu-regular-texture
(sdl-print-error)
(return 1)))
;; current-key-states is owned by SDL, but we own last-frame-states
(defer (dynarray-free (field s-key-states last-frame-states)))
@ -53,6 +209,24 @@
(when (keybind-tapped (addr s-quit-keybind) (addr s-key-states))
(set exit-reason "Quit keybind pressed"))
(SDL_SetRenderDrawColor renderer 85 85 85 255)
(SDL_RenderClear renderer)
(scope
(var source-rectangle SDL_Rect
(array 0 0
(field ubuntu-regular-font-atlas width)
(field ubuntu-regular-font-atlas height)))
(var destination-rectangle SDL_Rect
(array 0 0
(field ubuntu-regular-font-atlas width)
(field ubuntu-regular-font-atlas height)))
(unless (= 0 (SDL_RenderCopy renderer ubuntu-regular-texture
(addr source-rectangle) (addr destination-rectangle)))
(sdl-print-error)
(set exit-reason "SDL failed to render font atlas")))
(SDL_RenderPresent renderer)
(SDL_UpdateWindowSurface window)
(dynarray-set-length (field s-key-states last-frame-states) num-keys)

Loading…
Cancel
Save