render: Add bitmap font support.
authorDavid Thompson <dthompson2@worcester.edu>
Wed, 25 Jan 2017 01:16:08 +0000 (20:16 -0500)
committerDavid Thompson <dthompson2@worcester.edu>
Wed, 25 Jan 2017 02:25:20 +0000 (21:25 -0500)
* chickadee/render/font.scm: New file.
* Makefile.am (SOURCES): Add it.
* examples/text.scm: New file.
* examples/fonts/AUTHORS: New file.
* examples/fonts/good_neighbors_starling.png: New file.
* examples/fonts/good_neighbors_starling.xml: New file.

Makefile.am
chickadee/render/font.scm [new file with mode: 0644]
examples/fonts/AUTHORS [new file with mode: 0644]
examples/fonts/good_neighbors_starling.png [new file with mode: 0644]
examples/fonts/good_neighbors_starling.xml [new file with mode: 0644]
examples/text.scm [new file with mode: 0644]

index 6dadc1b..8e65d44 100644 (file)
@@ -57,6 +57,7 @@ SOURCES =                                     \
   chickadee/render/framebuffer.scm             \
   chickadee/render/shapes.scm                  \
   chickadee/render/sprite.scm                  \
+  chickadee/render/font.scm                    \
   chickadee/render.scm                         \
   chickadee/window.scm                         \
   chickadee.scm
diff --git a/chickadee/render/font.scm b/chickadee/render/font.scm
new file mode 100644 (file)
index 0000000..8f89b95
--- /dev/null
@@ -0,0 +1,191 @@
+;;; Chickadee Game Toolkit
+;;; Copyright © 2017 David Thompson <davet@gnu.org>
+;;;
+;;; Chickadee is free software: you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published
+;;; by the Free Software Foundation, either version 3 of the License,
+;;; or (at your option) any later version.
+;;;
+;;; Chickadee is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;;; General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with this program.  If not, see
+;;; <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; Bitmap font rendering.
+;;
+;;; Code:
+
+(define-module (chickadee render font)
+  #:use-module (ice-9 format)
+  #:use-module (srfi srfi-9)
+  #:use-module (srfi srfi-9 gnu)
+  #:use-module (srfi srfi-11)
+  #:use-module (sxml xpath)
+  #:use-module (sxml simple)
+  #:use-module (chickadee math matrix)
+  #:use-module (chickadee math rect)
+  #:use-module (chickadee math vector)
+  #:use-module (chickadee render)
+  #:use-module (chickadee render shader)
+  #:use-module (chickadee render sprite)
+  #:use-module (chickadee render texture)
+  #:export (load-font
+            font?
+            font-face
+            font-line-height
+            font-bold?
+            font-italic?
+            draw-text))
+
+(define-record-type <font-char>
+  (make-font-char id texture region offset dimensions advance)
+  font-char?
+  (id font-char-id)
+  (texture font-char-texture)
+  (region font-char-region)
+  (offset font-char-offset)
+  (dimensions font-char-dimensions)
+  (advance font-char-advance))
+
+(define-record-type <font>
+  (make-font face bold? italic? line-height chars kerning)
+  font?
+  (face font-face)
+  (bold? font-bold?)
+  (italic? font-italic?)
+  (line-height font-line-height)
+  (chars font-chars)
+  (kerning font-kerning))
+
+(define (display-font font port)
+  (format port "#<font face: ~a line-height: ~d bold?: ~a italic?: ~a>"
+          (font-face font)
+          (font-line-height font)
+          (font-bold? font)
+          (font-italic? font)))
+
+(set-record-type-printer! <font> display-font)
+
+(define (load-font file)
+  "Load the Angel Code XML formatted font within FILE."
+  (define directory (dirname file))
+  (define* (attr tree name #:optional (parse identity))
+    (let ((result ((sxpath `(@ ,name *text*)) tree)))
+      (if (null? result)
+          #f
+          (parse (car result)))))
+  (define (parse-pages nodes)
+    (let ((table (make-hash-table)))
+      (for-each (lambda (node)
+                  (let* ((id (attr node 'id string->number))
+                         (file (attr node 'file))
+                         (texture (load-image
+                                   (string-append directory "/" file))))
+                    (hash-set! table id texture)))
+                nodes)
+      table))
+  (define (string->character s)
+    (integer->char (string->number s)))
+  (define (parse-chars nodes pages image-width image-height line-height)
+    (define (x->s x)
+      (exact->inexact (/ x image-width)))
+    (define (y->t y)
+      (exact->inexact (/ y image-height)))
+    (let ((table (make-hash-table)))
+      (for-each (lambda (node)
+                  (let* ((id (attr node 'id string->character))
+                         (width (attr node 'width string->number))
+                         (height (attr node 'height string->number))
+                         (x (attr node 'x string->number))
+                         ;; Invert the y axis.  Our origin is the
+                         ;; bottom-left corner, not top-left.
+                         (y (- image-height height
+                               (attr node 'y string->number)))
+                         (x-offset (attr node 'xoffset string->number))
+                         (y-offset (- line-height height
+                                      (attr node 'yoffset string->number)))
+                         (x-advance (attr node 'xadvance string->number))
+                         (page (or (attr node 'page string->number) 0))
+                         (region (make-rect (x->s x)
+                                            (y->t y)
+                                            (x->s width)
+                                            (y->t height)))
+                         (char (make-font-char id
+                                               (hash-ref pages page)
+                                               region
+                                               (vec2 x-offset y-offset)
+                                               (vec2 width height)
+                                               (vec2 x-advance 0.0))))
+                    (hash-set! table id char)))
+                nodes)
+      table))
+  (define (parse-kernings nodes)
+    (let ((table (make-hash-table)))
+      (for-each (lambda (node)
+                  (let* ((first (attr node 'first string->character))
+                         (second (attr node 'second string->character))
+                         (x-offset (attr node 'amount string->number))
+                         (inner-table (hash-ref table first)))
+                    (if inner-table
+                        (hash-set! inner-table second (vec2 x-offset 0.0))
+                        (let ((inner-table (make-hash-table)))
+                          (hash-set! inner-table second (vec2 x-offset 0.0))
+                          (hash-set! table first inner-table)))))
+                nodes)
+      table))
+  (let* ((tree (call-with-input-file file xml->sxml))
+         (info ((sxpath '(font info)) tree))
+         (common ((sxpath '(font common)) tree))
+         (face (attr info 'face))
+         (bold? (attr info 'bold (const #t)))
+         (italic? (attr info 'italic (const #t)))
+         (line-height (attr common 'lineHeight string->number))
+         (image-width (attr common 'scaleW string->number))
+         (image-height (attr common 'scaleH string->number))
+         (pages (parse-pages ((sxpath '(font pages page)) tree)))
+         (chars (parse-chars ((sxpath '(font chars char)) tree)
+                             pages
+                             image-width
+                             image-height
+                             line-height))
+         (kernings (parse-kernings ((sxpath '(font kernings kerning)) tree))))
+    (make-font face bold? italic? line-height chars kernings)))
+
+(define (font-ref font char)
+  (hashv-ref (font-chars font) char))
+
+(define draw-text
+  (let ((p (vec2 0.0 0.0))
+        (rect (make-rect 0.0 0.0 0.0 0.0)))
+    (lambda* (font text position #:key scale rotation (blend-mode 'alpha))
+      "Draw the string TEXT with the first character starting at
+POSITION using FONT."
+      ;; TODO: Respect kerning.
+      (define (render-char c)
+        (let* ((char (font-ref font c))
+               (dimensions (font-char-dimensions char))
+               (offset (font-char-offset char)))
+          (set-rect-x! rect (+ (vec2-x p) (vec2-x offset)))
+          (set-rect-y! rect (+ (vec2-y p) (vec2-y offset)))
+          (set-rect-width! rect (vec2-x dimensions))
+          (set-rect-height! rect (vec2-y dimensions))
+          (draw-sprite (font-char-texture char)
+                       rect
+                       #:texture-region (font-char-region char)
+                       #:scale scale
+                       #:rotation rotation
+                       #:blend-mode blend-mode)
+          ;; Move forward to where the next character needs to be drawn.
+          (set-vec2-x! p
+                       (+ (vec2-x p)
+                          (vec2-x
+                           (font-char-advance char))))))
+      (vec2-copy! position p) ; initialize position
+      (with-batched-sprites
+       (string-for-each render-char text)))))
diff --git a/examples/fonts/AUTHORS b/examples/fonts/AUTHORS
new file mode 100644 (file)
index 0000000..13a3415
--- /dev/null
@@ -0,0 +1,5 @@
+-*- org -*-
+
+* good_neighbors_starling.png, good_neighbors_starling.xml
+  By PROWNE and Clint Bellanger, CC0
+  http://opengameart.org/content/good-neighbors-pixel-font-starlingunity-version-updated
diff --git a/examples/fonts/good_neighbors_starling.png b/examples/fonts/good_neighbors_starling.png
new file mode 100644 (file)
index 0000000..74197a6
Binary files /dev/null and b/examples/fonts/good_neighbors_starling.png differ
diff --git a/examples/fonts/good_neighbors_starling.xml b/examples/fonts/good_neighbors_starling.xml
new file mode 100644 (file)
index 0000000..0a7afde
--- /dev/null
@@ -0,0 +1,244 @@
+<font>\r
+       <info face="GoodNeighbors" size="16" />\r
+       <common lineHeight="16" scaleW="167" scaleH="168" pages="1" />\r
+       <pages>\r
+               <page id="0" file="good_neighbors_starling.png" />\r
+       </pages>\r
+       <chars count="233">\r
+               <char id="33" x="153" y="26" width="6" height="12" xoffset="0" yoffset="4" xadvance="5" /><!-- ! -->\r
+               <char id="34" x="82" y="32" width="7" height="7" xoffset="0" yoffset="4" xadvance="6" /><!-- " -->\r
+               <char id="35" x="24" y="77" width="10" height="10" xoffset="0" yoffset="5" xadvance="9" /><!-- # -->\r
+               <char id="36" x="24" y="88" width="10" height="14" xoffset="0" yoffset="3" xadvance="9" /><!-- $ -->\r
+               <char id="37" x="12" y="124" width="11" height="12" xoffset="0" yoffset="4" xadvance="10" /><!-- % -->\r
+               <char id="38" x="0" y="146" width="11" height="12" xoffset="0" yoffset="4" xadvance="10" /><!-- & -->\r
+               <char id="39" x="45" y="136" width="4" height="7" xoffset="0" yoffset="4" xadvance="3" /><!-- ' -->\r
+               <char id="40" x="160" y="26" width="6" height="14" xoffset="0" yoffset="3" xadvance="5" /><!-- ( -->\r
+               <char id="41" x="160" y="41" width="6" height="14" xoffset="0" yoffset="3" xadvance="5" /><!-- ) -->\r
+               <char id="42" x="24" y="126" width="10" height="11" xoffset="0" yoffset="3" xadvance="9" /><!-- * -->\r
+               <char id="43" x="126" y="156" width="8" height="9" xoffset="0" yoffset="6" xadvance="7" /><!-- + -->\r
+               <char id="44" x="135" y="160" width="4" height="7" xoffset="0" yoffset="11" xadvance="3" /><!-- , -->\r
+               <char id="45" x="0" y="160" width="9" height="5" xoffset="0" yoffset="8" xadvance="8" /><!-- - -->\r
+               <char id="46" x="43" y="161" width="4" height="5" xoffset="0" yoffset="11" xadvance="3" /><!-- . -->\r
+               <char id="47" x="144" y="13" width="8" height="14" xoffset="0" yoffset="3" xadvance="7" /><!-- / -->\r
+               <char id="48" x="136" y="0" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- 0 -->\r
+               <char id="49" x="160" y="13" width="6" height="12" xoffset="0" yoffset="4" xadvance="5" /><!-- 1 -->\r
+               <char id="50" x="144" y="54" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- 2 -->\r
+               <char id="51" x="144" y="80" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- 3 -->\r
+               <char id="52" x="45" y="26" width="9" height="12" xoffset="0" yoffset="4" xadvance="8" /><!-- 4 -->\r
+               <char id="53" x="135" y="78" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- 5 -->\r
+               <char id="54" x="144" y="28" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- 6 -->\r
+               <char id="55" x="144" y="41" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- 7 -->\r
+               <char id="56" x="144" y="93" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- 8 -->\r
+               <char id="57" x="144" y="132" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- 9 -->\r
+               <char id="58" x="160" y="158" width="4" height="8" xoffset="0" yoffset="7" xadvance="3" /><!-- : -->\r
+               <char id="59" x="161" y="0" width="4" height="10" xoffset="0" yoffset="8" xadvance="3" /><!-- ; -->\r
+               <char id="60" x="44" y="145" width="9" height="14" xoffset="0" yoffset="3" xadvance="8" /><!-- < -->\r
+               <char id="61" x="117" y="159" width="7" height="8" xoffset="0" yoffset="7" xadvance="6" /><!-- = -->\r
+               <char id="62" x="35" y="81" width="9" height="14" xoffset="0" yoffset="3" xadvance="8" /><!-- > -->\r
+               <char id="63" x="126" y="89" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- ? -->\r
+               <char id="64" x="13" y="86" width="10" height="12" xoffset="0" yoffset="4" xadvance="9" /><!-- @ -->\r
+               <char id="65" x="144" y="119" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- A -->\r
+               <char id="66" x="144" y="106" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- B -->\r
+               <char id="67" x="144" y="67" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- C -->\r
+               <char id="68" x="36" y="0" width="9" height="12" xoffset="0" yoffset="4" xadvance="8" /><!-- D -->\r
+               <char id="69" x="135" y="13" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- E -->\r
+               <char id="70" x="127" y="0" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- F -->\r
+               <char id="71" x="126" y="117" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- G -->\r
+               <char id="72" x="135" y="130" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- H -->\r
+               <char id="73" x="153" y="67" width="6" height="12" xoffset="0" yoffset="4" xadvance="5" /><!-- I -->\r
+               <char id="74" x="35" y="13" width="9" height="12" xoffset="0" yoffset="4" xadvance="8" /><!-- J -->\r
+               <char id="75" x="135" y="91" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- K -->\r
+               <char id="76" x="135" y="143" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- L -->\r
+               <char id="77" x="13" y="46" width="10" height="12" xoffset="0" yoffset="4" xadvance="9" /><!-- M -->\r
+               <char id="78" x="35" y="119" width="9" height="12" xoffset="0" yoffset="4" xadvance="8" /><!-- N -->\r
+               <char id="79" x="135" y="117" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- O -->\r
+               <char id="80" x="135" y="104" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- P -->\r
+               <char id="81" x="45" y="52" width="9" height="13" xoffset="0" yoffset="4" xadvance="8" /><!-- Q -->\r
+               <char id="82" x="34" y="148" width="9" height="12" xoffset="0" yoffset="4" xadvance="8" /><!-- R -->\r
+               <char id="83" x="135" y="65" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- S -->\r
+               <char id="84" x="135" y="52" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- T -->\r
+               <char id="85" x="135" y="39" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- U -->\r
+               <char id="86" x="135" y="26" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- V -->\r
+               <char id="87" x="24" y="113" width="10" height="12" xoffset="0" yoffset="4" xadvance="9" /><!-- W -->\r
+               <char id="88" x="26" y="0" width="9" height="12" xoffset="0" yoffset="4" xadvance="8" /><!-- X -->\r
+               <char id="89" x="126" y="143" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- Y -->\r
+               <char id="90" x="126" y="130" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- Z -->\r
+               <char id="91" x="153" y="109" width="6" height="14" xoffset="0" yoffset="3" xadvance="5" /><!-- [ -->\r
+               <char id="92" x="126" y="102" width="8" height="14" xoffset="0" yoffset="3" xadvance="7" /><!-- \ -->\r
+               <char id="93" x="153" y="39" width="6" height="14" xoffset="0" yoffset="3" xadvance="5" /><!-- ] -->\r
+               <char id="94" x="0" y="124" width="11" height="8" xoffset="0" yoffset="3" xadvance="10" /><!-- ^ -->\r
+               <char id="95" x="34" y="161" width="8" height="5" xoffset="0" yoffset="12" xadvance="7" /><!-- _ -->\r
+               <char id="96" x="127" y="13" width="6" height="6" xoffset="0" yoffset="4" xadvance="5" /><!-- ` -->\r
+               <char id="97" x="126" y="56" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- a -->\r
+               <char id="98" x="126" y="43" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- b -->\r
+               <char id="99" x="126" y="33" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- c -->\r
+               <char id="100" x="126" y="20" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- d -->\r
+               <char id="101" x="118" y="10" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- e -->\r
+               <char id="102" x="145" y="0" width="7" height="12" xoffset="0" yoffset="4" xadvance="6" /><!-- f -->\r
+               <char id="103" x="117" y="146" width="8" height="12" xoffset="0" yoffset="7" xadvance="7" /><!-- g -->\r
+               <char id="104" x="117" y="133" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- h -->\r
+               <char id="105" x="153" y="96" width="6" height="12" xoffset="0" yoffset="4" xadvance="5" /><!-- i -->\r
+               <char id="106" x="153" y="80" width="6" height="15" xoffset="0" yoffset="4" xadvance="5" /><!-- j -->\r
+               <char id="107" x="117" y="94" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- k -->\r
+               <char id="108" x="160" y="130" width="5" height="12" xoffset="0" yoffset="4" xadvance="4" /><!-- l -->\r
+               <char id="109" x="24" y="67" width="10" height="9" xoffset="0" yoffset="7" xadvance="9" /><!-- m -->\r
+               <char id="110" x="117" y="61" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- n -->\r
+               <char id="111" x="117" y="51" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- o -->\r
+               <char id="112" x="117" y="38" width="8" height="12" xoffset="0" yoffset="7" xadvance="7" /><!-- p -->\r
+               <char id="113" x="35" y="26" width="9" height="12" xoffset="0" yoffset="7" xadvance="8" /><!-- q -->\r
+               <char id="114" x="109" y="12" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- r -->\r
+               <char id="115" x="108" y="157" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- s -->\r
+               <char id="116" x="109" y="0" width="8" height="11" xoffset="0" yoffset="5" xadvance="7" /><!-- t -->\r
+               <char id="117" x="108" y="147" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- u -->\r
+               <char id="118" x="108" y="137" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- v -->\r
+               <char id="119" x="24" y="103" width="10" height="9" xoffset="0" yoffset="7" xadvance="9" /><!-- w -->\r
+               <char id="120" x="108" y="117" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- x -->\r
+               <char id="121" x="108" y="104" width="8" height="12" xoffset="0" yoffset="7" xadvance="7" /><!-- y -->\r
+               <char id="122" x="108" y="94" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- z -->\r
+               <char id="123" x="144" y="145" width="7" height="14" xoffset="0" yoffset="3" xadvance="6" /><!-- { -->\r
+               <char id="124" x="160" y="143" width="4" height="14" xoffset="0" yoffset="3" xadvance="3" /><!-- | -->\r
+               <char id="125" x="152" y="145" width="7" height="14" xoffset="0" yoffset="3" xadvance="6" /><!-- } -->\r
+               <char id="126" x="10" y="160" width="9" height="6" xoffset="0" yoffset="4" xadvance="8" /><!-- ~ -->\r
+               <char id="1040" x="108" y="39" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- А -->\r
+               <char id="1041" x="108" y="26" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- Б -->\r
+               <char id="1042" x="100" y="13" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- В -->\r
+               <char id="1043" x="100" y="0" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- Г -->\r
+               <char id="1044" x="13" y="59" width="10" height="13" xoffset="0" yoffset="4" xadvance="9" /><!-- Д -->\r
+               <char id="1045" x="99" y="136" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- Е -->\r
+               <char id="1025" x="99" y="120" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Ё -->\r
+               <char id="1046" x="0" y="27" width="12" height="12" xoffset="0" yoffset="4" xadvance="11" /><!-- Ж -->\r
+               <char id="1047" x="99" y="94" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- З -->\r
+               <char id="1048" x="35" y="39" width="9" height="12" xoffset="0" yoffset="4" xadvance="8" /><!-- И -->\r
+               <char id="1049" x="35" y="52" width="9" height="15" xoffset="0" yoffset="1" xadvance="8" /><!-- Й -->\r
+               <char id="1050" x="99" y="65" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- К -->\r
+               <char id="1051" x="35" y="68" width="9" height="12" xoffset="0" yoffset="4" xadvance="8" /><!-- Л -->\r
+               <char id="1052" x="13" y="73" width="10" height="12" xoffset="0" yoffset="4" xadvance="9" /><!-- М -->\r
+               <char id="1053" x="99" y="26" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- Н -->\r
+               <char id="1054" x="91" y="13" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- О -->\r
+               <char id="1055" x="91" y="0" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- П -->\r
+               <char id="1056" x="90" y="145" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- Р -->\r
+               <char id="1057" x="90" y="132" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- С -->\r
+               <char id="1058" x="90" y="119" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- Т -->\r
+               <char id="1059" x="126" y="76" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- У -->\r
+               <char id="1060" x="0" y="14" width="12" height="12" xoffset="0" yoffset="4" xadvance="11" /><!-- Ф -->\r
+               <char id="1061" x="35" y="132" width="9" height="12" xoffset="0" yoffset="4" xadvance="8" /><!-- Х -->\r
+               <char id="1062" x="13" y="99" width="10" height="13" xoffset="0" yoffset="4" xadvance="9" /><!-- Ц -->\r
+               <char id="1063" x="90" y="48" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- Ч -->\r
+               <char id="1064" x="0" y="40" width="12" height="12" xoffset="0" yoffset="4" xadvance="11" /><!-- Ш -->\r
+               <char id="1065" x="0" y="0" width="14" height="13" xoffset="0" yoffset="4" xadvance="13" /><!-- Щ -->\r
+               <char id="1066" x="15" y="0" width="10" height="12" xoffset="0" yoffset="4" xadvance="9" /><!-- Ъ -->\r
+               <char id="1067" x="0" y="133" width="11" height="12" xoffset="0" yoffset="4" xadvance="10" /><!-- Ы -->\r
+               <char id="1068" x="81" y="128" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- Ь -->\r
+               <char id="1069" x="81" y="115" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- Э -->\r
+               <char id="1070" x="12" y="137" width="11" height="12" xoffset="0" yoffset="4" xadvance="10" /><!-- Ю -->\r
+               <char id="1071" x="45" y="13" width="9" height="12" xoffset="0" yoffset="4" xadvance="8" /><!-- Я -->\r
+               <char id="1072" x="81" y="76" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- а -->\r
+               <char id="1073" x="81" y="62" width="8" height="13" xoffset="0" yoffset="3" xadvance="7" /><!-- б -->\r
+               <char id="1074" x="81" y="52" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- в -->\r
+               <char id="1075" x="81" y="42" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- г -->\r
+               <char id="1076" x="13" y="113" width="10" height="9" xoffset="0" yoffset="7" xadvance="9" /><!-- д -->\r
+               <char id="1077" x="72" y="157" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- е -->\r
+               <char id="1105" x="73" y="16" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- ё -->\r
+               <char id="1078" x="24" y="24" width="10" height="9" xoffset="0" yoffset="7" xadvance="9" /><!-- ж -->\r
+               <char id="1079" x="72" y="147" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- з -->\r
+               <char id="1080" x="25" y="13" width="9" height="9" xoffset="0" yoffset="7" xadvance="8" /><!-- и -->\r
+               <char id="1081" x="35" y="96" width="9" height="12" xoffset="0" yoffset="4" xadvance="8" /><!-- й -->\r
+               <char id="1082" x="108" y="127" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- к -->\r
+               <char id="1083" x="35" y="109" width="9" height="9" xoffset="0" yoffset="7" xadvance="8" /><!-- л -->\r
+               <char id="1084" x="24" y="34" width="10" height="9" xoffset="0" yoffset="7" xadvance="9" /><!-- м -->\r
+               <char id="1085" x="90" y="158" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- н -->\r
+               <char id="1086" x="117" y="123" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- о -->\r
+               <char id="1087" x="117" y="84" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- п -->\r
+               <char id="1088" x="117" y="71" width="8" height="12" xoffset="0" yoffset="7" xadvance="7" /><!-- р -->\r
+               <char id="1089" x="81" y="157" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- с -->\r
+               <char id="1090" x="126" y="66" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- т -->\r
+               <char id="1091" x="63" y="142" width="8" height="12" xoffset="0" yoffset="7" xadvance="7" /><!-- у -->\r
+               <char id="1092" x="24" y="54" width="10" height="12" xoffset="0" yoffset="7" xadvance="9" /><!-- ф -->\r
+               <char id="1093" x="118" y="0" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- х -->\r
+               <char id="1094" x="13" y="35" width="10" height="10" xoffset="0" yoffset="7" xadvance="9" /><!-- ц -->\r
+               <char id="1095" x="63" y="88" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- ч -->\r
+               <char id="1096" x="24" y="138" width="10" height="9" xoffset="0" yoffset="7" xadvance="9" /><!-- ш -->\r
+               <char id="1097" x="0" y="90" width="12" height="10" xoffset="0" yoffset="7" xadvance="11" /><!-- щ -->\r
+               <char id="1098" x="24" y="44" width="10" height="9" xoffset="0" yoffset="7" xadvance="9" /><!-- ъ -->\r
+               <char id="1099" x="12" y="150" width="11" height="9" xoffset="0" yoffset="7" xadvance="10" /><!-- ы -->\r
+               <char id="1100" x="55" y="23" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- ь -->\r
+               <char id="1101" x="55" y="13" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- э -->\r
+               <char id="1102" x="13" y="14" width="11" height="9" xoffset="0" yoffset="7" xadvance="10" /><!-- ю -->\r
+               <char id="1103" x="54" y="156" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- я -->\r
+               <char id="1028" x="72" y="134" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- Є -->\r
+               <char id="1030" x="153" y="13" width="6" height="12" xoffset="0" yoffset="4" xadvance="5" /><!-- І -->\r
+               <char id="1031" x="54" y="111" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Ї -->\r
+               <char id="1038" x="63" y="98" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Ў -->\r
+               <char id="1168" x="63" y="114" width="8" height="14" xoffset="0" yoffset="2" xadvance="7" /><!-- Ґ -->\r
+               <char id="1108" x="55" y="33" width="8" height="9" xoffset="0" yoffset="7" xadvance="7" /><!-- є -->\r
+               <char id="1110" x="153" y="54" width="6" height="12" xoffset="0" yoffset="4" xadvance="5" /><!-- і -->\r
+               <char id="1111" x="153" y="0" width="7" height="12" xoffset="0" yoffset="4" xadvance="6" /><!-- ї -->\r
+               <char id="1118" x="54" y="127" width="8" height="15" xoffset="0" yoffset="4" xadvance="7" /><!-- ў -->\r
+               <char id="1169" x="45" y="95" width="8" height="11" xoffset="0" yoffset="5" xadvance="7" /><!-- ґ -->\r
+               <char id="196" x="54" y="82" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Ä -->\r
+               <char id="228" x="46" y="0" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- ä -->\r
+               <char id="192" x="45" y="66" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- À -->\r
+               <char id="224" x="45" y="107" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- à -->\r
+               <char id="194" x="45" y="120" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Â -->\r
+               <char id="226" x="45" y="82" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- â -->\r
+               <char id="193" x="54" y="66" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Á -->\r
+               <char id="225" x="54" y="98" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- á -->\r
+               <char id="195" x="63" y="72" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Ã -->\r
+               <char id="227" x="54" y="143" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- ã -->\r
+               <char id="197" x="64" y="32" width="8" height="16" xoffset="0" yoffset="0" xadvance="7" /><!-- Å -->\r
+               <char id="229" x="72" y="49" width="8" height="13" xoffset="0" yoffset="3" xadvance="7" /><!-- å -->\r
+               <char id="198" x="0" y="77" width="12" height="12" xoffset="0" yoffset="4" xadvance="11" /><!-- Æ -->\r
+               <char id="230" x="0" y="114" width="12" height="9" xoffset="0" yoffset="7" xadvance="11" /><!-- æ -->\r
+               <char id="200" x="72" y="118" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- È -->\r
+               <char id="232" x="63" y="155" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- è -->\r
+               <char id="202" x="64" y="0" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Ê -->\r
+               <char id="234" x="99" y="52" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- ê -->\r
+               <char id="201" x="99" y="78" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- É -->\r
+               <char id="233" x="72" y="63" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- é -->\r
+               <char id="199" x="108" y="52" width="8" height="15" xoffset="0" yoffset="4" xadvance="7" /><!-- Ç -->\r
+               <char id="231" x="72" y="105" width="8" height="12" xoffset="0" yoffset="7" xadvance="7" /><!-- ç -->\r
+               <char id="286" x="55" y="43" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Ğ -->\r
+               <char id="287" x="82" y="16" width="8" height="15" xoffset="0" yoffset="4" xadvance="7" /><!-- ğ -->\r
+               <char id="206" x="160" y="56" width="6" height="15" xoffset="0" yoffset="1" xadvance="5" /><!-- Î -->\r
+               <char id="238" x="160" y="104" width="6" height="12" xoffset="0" yoffset="4" xadvance="5" /><!-- î -->\r
+               <char id="205" x="160" y="88" width="6" height="15" xoffset="0" yoffset="1" xadvance="5" /><!-- Í -->\r
+               <char id="237" x="160" y="117" width="6" height="12" xoffset="0" yoffset="4" xadvance="5" /><!-- í -->\r
+               <char id="304" x="160" y="72" width="6" height="15" xoffset="0" yoffset="1" xadvance="5" /><!-- İ -->\r
+               <char id="305" x="64" y="49" width="6" height="9" xoffset="0" yoffset="7" xadvance="5" /><!-- ı -->\r
+               <char id="209" x="24" y="148" width="9" height="16" xoffset="0" yoffset="0" xadvance="8" /><!-- Ñ -->\r
+               <char id="241" x="63" y="129" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- ñ -->\r
+               <char id="214" x="64" y="16" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Ö -->\r
+               <char id="246" x="108" y="81" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- ö -->\r
+               <char id="210" x="73" y="0" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Ò -->\r
+               <char id="242" x="73" y="29" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- ò -->\r
+               <char id="212" x="82" y="0" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Ô -->\r
+               <char id="244" x="90" y="61" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- ô -->\r
+               <char id="211" x="90" y="90" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Ó -->\r
+               <char id="243" x="99" y="39" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- ó -->\r
+               <char id="213" x="99" y="149" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Õ -->\r
+               <char id="245" x="108" y="68" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- õ -->\r
+               <char id="338" x="0" y="101" width="12" height="12" xoffset="0" yoffset="4" xadvance="11" /><!-- Œ -->\r
+               <char id="339" x="0" y="67" width="12" height="9" xoffset="0" yoffset="7" xadvance="11" /><!-- œ -->\r
+               <char id="216" x="0" y="53" width="12" height="13" xoffset="0" yoffset="3" xadvance="11" /><!-- Ø -->\r
+               <char id="248" x="13" y="24" width="10" height="10" xoffset="0" yoffset="6" xadvance="9" /><!-- ø -->\r
+               <char id="350" x="117" y="107" width="8" height="15" xoffset="0" yoffset="4" xadvance="7" /><!-- Ş -->\r
+               <char id="351" x="90" y="106" width="8" height="12" xoffset="0" yoffset="7" xadvance="7" /><!-- ş -->\r
+               <char id="220" x="90" y="74" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Ü -->\r
+               <char id="252" x="72" y="92" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- ü -->\r
+               <char id="217" x="72" y="76" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Ù -->\r
+               <char id="249" x="55" y="0" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- ù -->\r
+               <char id="219" x="81" y="86" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Û -->\r
+               <char id="251" x="63" y="59" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- û -->\r
+               <char id="218" x="117" y="22" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Ú -->\r
+               <char id="250" x="81" y="102" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- ú -->\r
+               <char id="376" x="81" y="141" width="8" height="15" xoffset="0" yoffset="1" xadvance="7" /><!-- Ÿ -->\r
+               <char id="255" x="90" y="32" width="8" height="15" xoffset="0" yoffset="4" xadvance="7" /><!-- ÿ -->\r
+               <char id="191" x="99" y="107" width="8" height="12" xoffset="0" yoffset="4" xadvance="7" /><!-- ¿ -->\r
+               <char id="161" x="153" y="124" width="6" height="12" xoffset="0" yoffset="4" xadvance="5" /><!-- ¡ -->\r
+               <char id="223" x="45" y="39" width="9" height="12" xoffset="0" yoffset="4" xadvance="8" /><!-- ß -->\r
+               <char id="32" x="0" y="0" width="0" height="0" xoffset="0" yoffset="0" xadvance="4" /><!--   -->\r
+               <char id="9" x="0" y="0" width="0" height="0" xoffset="0" yoffset="0" xadvance="32" /><!--       -->\r
+       </chars>\r
+       <kernings count="0">\r
+       </kernings>\r
+</font>\r
diff --git a/examples/text.scm b/examples/text.scm
new file mode 100644 (file)
index 0000000..37ac22c
--- /dev/null
@@ -0,0 +1,26 @@
+(use-modules (chickadee)
+             (chickadee math vector)
+             (chickadee render font))
+
+(define font #f)
+
+(define (load)
+  (set! font (load-font "fonts/good_neighbors_starling.xml")))
+
+(define (draw alpha)
+  (draw-text font "(draw-text font \"Hello, world!\" (vec2 100.0 200.0))"
+             (vec2 100.0 200.0))
+  (draw-text font "Chickadee is a game development toolkit for Guile Scheme."
+             (vec2 100.0 150.0))
+  (draw-text font "The quick brown fox jumps over the lazy dog"
+             (vec2 100.0 100.0)))
+
+(add-hook! load-hook load)
+(add-hook! draw-hook draw)
+(add-hook! quit-hook abort-game)
+(add-hook! key-press-hook
+           (lambda (key scan modifiers repeat?)
+             (when (eq? key 'q)
+               (abort-game))))
+
+(run-game)