diff --git a/README.md b/README.md
index 22abbbeaf90e57eca25b4a08bc2c340a5dd3cdf5..c079fbb7d3d3fc59f2320628cf38db89df783994 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,10 @@
 # OSX
 
-clang++ -std=c++17 -L./osx/lib/ -lSDL2 -I./osx/include/ -I./include src/context.cpp src/app.cpp main.cpp -framework OpenGL
+clang++ -std=c++17 -L./osx/lib/ -lSDL2 -lSDL2_ttf -lSDL2_image -lfreetype -lpng -lwebp -ltiff -ljpeg -lbz2 -lz -I./osx/include/ -I./osx/include/SDL2/ -I./include ./SDL_FontCache.o src/context.cpp src/app.cpp examples/splat.cpp -framework OpenGL
 
+We need to build font cache
+
+clang -c -I./osx/include/ -I./osx/include/SDL2/  -I./include src/SDL_FontCache.c
 
 # Windows
 
diff --git a/SDL2_image.dll b/SDL2_image.dll
new file mode 100755
index 0000000000000000000000000000000000000000..d7016555aee06c8480f66c2cb1ad5ed15c8f1856
Binary files /dev/null and b/SDL2_image.dll differ
diff --git a/assets/ZX_Spectrum_character_set.png b/assets/ZX_Spectrum_character_set.png
new file mode 100644
index 0000000000000000000000000000000000000000..9af7b56278d2ae614a5751932203162a8cd27984
Binary files /dev/null and b/assets/ZX_Spectrum_character_set.png differ
diff --git a/assets/fonts/FreeSans.ttf b/assets/fonts/FreeSans.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..2723a475ef8b69c19cc17c8078b044089d34527c
Binary files /dev/null and b/assets/fonts/FreeSans.ttf differ
diff --git a/examples/shapes.cpp b/examples/shapes.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..62a0e338d7dcac82956d9e201a6c08b296d83a95
--- /dev/null
+++ b/examples/shapes.cpp
@@ -0,0 +1,147 @@
+#include <iostream>
+#include <iomanip>
+#include <vector>
+#include <cstring>
+
+#include <context.hpp>
+#include <app.hpp>
+
+const int width = 640;
+const int height = 480;
+
+class MyApp: public uwe::App {
+private:
+    int width_;
+    int height_;
+    uwe::Image text_;
+    int text_width_;
+    int text_height_;
+
+    uwe::Image some_text_;
+
+    uwe::Font font15_;
+public:
+    MyApp(int width, int height);
+    ~MyApp();
+
+    void begin() override;
+    void update() override;
+    void draw() override;
+
+    void key_pressed(uwe::Scancode scancode, bool repeat) override;
+
+    uwe::Image render_text(std::string msg);
+};
+
+MyApp::MyApp(int width, int height): 
+    width_{width},
+    height_{height} {
+        init(width, height);
+}
+
+MyApp::~MyApp() {
+
+}
+
+void MyApp::begin() {
+    text_ = create_image("./assets/ZX_Spectrum_character_set.png");
+    get_image_size(text_, &text_width_, &text_height_);
+
+    some_text_ = render_text("abc");
+
+    font15_ = create_font("./assets/fonts/FreeSans.ttf", 15, uwe::Colour::red());
+}
+
+void MyApp::update() {
+
+}
+
+void MyApp::key_pressed(uwe::Scancode scancode, bool repeat) {
+    if( scancode == uwe::Scancode::L) {
+        toggle_framerate();
+    }
+}
+
+void MyApp::draw() {
+    clear(uwe::Colour::white());
+
+    set_draw_color(uwe::Colour::red());
+    draw_rect(20,20,100,100);
+
+    set_draw_color(uwe::Colour::blue());
+    draw_rect_fill(100,100,100,100);
+
+    draw_line(300, 300, 350, 400);
+
+    set_draw_color(uwe::Colour::green());
+    draw_point(400, 100);
+
+    draw_image(text_, uwe::make_rect(0,0,text_width_,text_height_), uwe::make_rect(200,200,text_width_,text_height_));
+
+    draw_font(font15_, "This is a msg", 300, 100);
+
+    int width;
+    int height;
+    get_image_size(some_text_, &width, &height);
+    draw_image(some_text_, uwe::make_rect(0,0, width, height), uwe::make_rect(150,30, width, height) );
+
+    
+}
+
+uwe::Image MyApp::render_text(std::string msg) {
+    int max_width = 0;
+	int num_lines = 1;
+	int current_width = 0;
+
+	for (auto const c : msg) {
+		if (c != '\n')
+			current_width++;
+		else {
+			num_lines++;
+			if (current_width > max_width)
+				max_width = current_width;
+			current_width = 0;
+		}
+	}
+	if (current_width > max_width) {
+		max_width = current_width;
+    }
+	int text_width = max_width * 16;
+	int text_height = num_lines * 16;
+
+    auto image = create_image(text_width, text_height);
+
+    set_draw_color(uwe::Colour::white());
+    set_renderer_taret(image, true);
+
+    int x = 0; int y = 0;
+	for (auto const c : msg) {
+		if (c != '\n') {
+			// Figure out what character it is
+			int code = c - 32;
+			if (c == '$') {
+				code = 64;
+            }
+			uwe::Rect src{ (code % 16)*16, (code / 16)*16, 16, 16 };
+			uwe::Rect dest{ x, y, 16, 16 };
+			draw_image(text_, src, dest);
+			x += 16;
+		} else {
+			x = 0;
+			y+=16;
+		}
+	}
+
+    set_renderer_taret(nullptr, true);
+
+    return image;
+}
+
+int main( int argc, char** argv ) {
+    uwe::App* app = new MyApp{width, height};
+
+    //app->dump_renderer_info();
+    app->run();
+
+    return 0;
+}
\ No newline at end of file
diff --git a/main.cpp b/examples/splat.cpp
similarity index 98%
rename from main.cpp
rename to examples/splat.cpp
index 738467ba722b71273d1bb52a178a7024030cd134..8add178b3e54f2937746a16afdd6ecff37d8be39 100644
--- a/main.cpp
+++ b/examples/splat.cpp
@@ -65,6 +65,8 @@ void MyApp::draw() {
                 static_cast<uint8_t>(rand() % 256), 
                 SDL_ALPHA_OPAQUE});
     }
+
+    blit_framebuffer();
 }
 
 
diff --git a/include/SDL_FontCache.h b/include/SDL_FontCache.h
new file mode 100644
index 0000000000000000000000000000000000000000..77f5fb4b644634cab358c8244ae6ef9fa8ea71d8
--- /dev/null
+++ b/include/SDL_FontCache.h
@@ -0,0 +1,327 @@
+/*
+SDL_FontCache v0.10.0: A font cache for SDL and SDL_ttf
+by Jonathan Dearborn
+Dedicated to the memory of Florian Hufsky
+
+License:
+    The short:
+    Use it however you'd like, but keep the copyright and license notice
+    whenever these files or parts of them are distributed in uncompiled form.
+
+    The long:
+Copyright (c) 2019 Jonathan Dearborn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#ifndef _SDL_FONTCACHE_H__
+#define _SDL_FONTCACHE_H__
+
+#include "SDL.h"
+#include "SDL_ttf.h"
+
+#ifdef FC_USE_SDL_GPU
+    #include "SDL_gpu.h"
+#endif
+
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+// Let's pretend this exists...
+#define TTF_STYLE_OUTLINE	16
+
+
+
+// Differences between SDL_Renderer and SDL_gpu
+#ifdef FC_USE_SDL_GPU
+#define FC_Rect GPU_Rect
+#define FC_Target GPU_Target
+#define FC_Image GPU_Image
+#define FC_Log GPU_LogError
+#else
+#define FC_Rect SDL_Rect
+#define FC_Target SDL_Renderer
+#define FC_Image SDL_Texture
+#define FC_Log SDL_Log
+#endif
+
+
+// SDL_FontCache types
+
+typedef enum
+{
+    FC_ALIGN_LEFT,
+    FC_ALIGN_CENTER,
+    FC_ALIGN_RIGHT
+} FC_AlignEnum;
+
+typedef enum
+{
+    FC_FILTER_NEAREST,
+    FC_FILTER_LINEAR
+} FC_FilterEnum;
+
+typedef struct FC_Scale
+{
+    float x;
+    float y;
+
+} FC_Scale;
+
+typedef struct FC_Effect
+{
+    FC_AlignEnum alignment;
+    FC_Scale scale;
+    SDL_Color color;
+
+} FC_Effect;
+
+// Opaque type
+typedef struct FC_Font FC_Font;
+
+
+typedef struct FC_GlyphData
+{
+    SDL_Rect rect;
+    int cache_level;
+
+} FC_GlyphData;
+
+
+
+
+// Object creation
+
+FC_Rect FC_MakeRect(float x, float y, float w, float h);
+
+FC_Scale FC_MakeScale(float x, float y);
+
+SDL_Color FC_MakeColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a);
+
+FC_Effect FC_MakeEffect(FC_AlignEnum alignment, FC_Scale scale, SDL_Color color);
+
+FC_GlyphData FC_MakeGlyphData(int cache_level, Sint16 x, Sint16 y, Uint16 w, Uint16 h);
+
+
+
+// Font object
+
+FC_Font* FC_CreateFont(void);
+
+#ifdef FC_USE_SDL_GPU
+Uint8 FC_LoadFont(FC_Font* font, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style);
+
+Uint8 FC_LoadFontFromTTF(FC_Font* font, TTF_Font* ttf, SDL_Color color);
+
+Uint8 FC_LoadFont_RW(FC_Font* font, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style);
+#else
+Uint8 FC_LoadFont(FC_Font* font, SDL_Renderer* renderer, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style);
+
+Uint8 FC_LoadFontFromTTF(FC_Font* font, SDL_Renderer* renderer, TTF_Font* ttf, SDL_Color color);
+
+Uint8 FC_LoadFont_RW(FC_Font* font, SDL_Renderer* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style);
+#endif
+
+#ifndef FC_USE_SDL_GPU
+// note: handle SDL event types SDL_RENDER_TARGETS_RESET(>= SDL 2.0.2) and SDL_RENDER_DEVICE_RESET(>= SDL 2.0.4)
+void FC_ResetFontFromRendererReset(FC_Font* font, SDL_Renderer* renderer, Uint32 evType);
+#endif
+
+void FC_ClearFont(FC_Font* font);
+
+void FC_FreeFont(FC_Font* font);
+
+
+
+// Built-in loading strings
+
+char* FC_GetStringASCII(void);
+
+char* FC_GetStringLatin1(void);
+
+char* FC_GetStringASCII_Latin1(void);
+
+
+// UTF-8 to SDL_FontCache codepoint conversion
+
+/*!
+Returns the Uint32 codepoint (not UTF-32) parsed from the given UTF-8 string.
+\param c A pointer to a string of proper UTF-8 character values.
+\param advance_pointer If true, the source pointer will be incremented to skip the extra bytes from multibyte codepoints.
+*/
+Uint32 FC_GetCodepointFromUTF8(const char** c, Uint8 advance_pointer);
+
+/*!
+Parses the given codepoint and stores the UTF-8 bytes in 'result'.  The result is NULL terminated.
+\param result A memory buffer for the UTF-8 values.  Must be at least 5 bytes long.
+\param codepoint The Uint32 codepoint to parse (not UTF-32).
+*/
+void FC_GetUTF8FromCodepoint(char* result, Uint32 codepoint);
+
+
+// UTF-8 string operations
+
+/*! Allocates a new string of 'size' bytes that is already NULL-terminated.  The NULL byte counts toward the size limit, as usual.  Returns NULL if size is 0. */
+char* U8_alloc(unsigned int size);
+
+/*! Deallocates the given string. */
+void U8_free(char* string);
+
+/*! Allocates a copy of the given string. */
+char* U8_strdup(const char* string);
+
+/*! Returns the number of UTF-8 characters in the given string. */
+int U8_strlen(const char* string);
+
+/*! Returns the number of bytes in the UTF-8 multibyte character pointed at by 'character'. */
+int U8_charsize(const char* character);
+
+/*! Copies the source multibyte character into the given buffer without overrunning it.  Returns 0 on failure. */
+int U8_charcpy(char* buffer, const char* source, int buffer_size);
+
+/*! Returns a pointer to the next UTF-8 character. */
+const char* U8_next(const char* string);
+
+/*! Inserts a UTF-8 string into 'string' at the given position.  Use a position of -1 to append.  Returns 0 when unable to insert the string. */
+int U8_strinsert(char* string, int position, const char* source, int max_bytes);
+
+/*! Erases the UTF-8 character at the given position, moving the subsequent characters down. */
+void U8_strdel(char* string, int position);
+
+
+// Internal settings
+
+/*! Sets the string from which to load the initial glyphs.  Use this if you need upfront loading for any reason (such as lack of render-target support). */
+void FC_SetLoadingString(FC_Font* font, const char* string);
+
+/*! Returns the size of the internal buffer which is used for unpacking variadic text data.  This buffer is shared by all FC_Fonts. */
+unsigned int FC_GetBufferSize(void);
+
+/*! Changes the size of the internal buffer which is used for unpacking variadic text data.  This buffer is shared by all FC_Fonts. */
+void FC_SetBufferSize(unsigned int size);
+
+/*! Returns the width of a single horizontal tab in multiples of the width of a space (default: 4) */
+unsigned int FC_GetTabWidth(void);
+
+/*! Changes the width of a horizontal tab in multiples of the width of a space (default: 4) */
+void FC_SetTabWidth(unsigned int width_in_spaces);
+
+void FC_SetRenderCallback(FC_Rect (*callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale));
+
+FC_Rect FC_DefaultRenderCallback(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale);
+
+
+// Custom caching
+
+/*! Returns the number of cache levels that are active. */
+int FC_GetNumCacheLevels(FC_Font* font);
+
+/*! Returns the cache source texture at the given cache level. */
+FC_Image* FC_GetGlyphCacheLevel(FC_Font* font, int cache_level);
+
+// TODO: Specify ownership of the texture (should be shareable)
+/*! Sets a cache source texture for rendering.  New cache levels must be sequential. */
+Uint8 FC_SetGlyphCacheLevel(FC_Font* font, int cache_level, FC_Image* cache_texture);
+
+/*! Copies the given surface to the given cache level as a texture.  New cache levels must be sequential. */
+Uint8 FC_UploadGlyphCache(FC_Font* font, int cache_level, SDL_Surface* data_surface);
+
+
+/*! Returns the number of codepoints that are stored in the font's glyph data map. */
+unsigned int FC_GetNumCodepoints(FC_Font* font);
+
+/*! Copies the stored codepoints into the given array. */
+void FC_GetCodepoints(FC_Font* font, Uint32* result);
+
+/*! Stores the glyph data for the given codepoint in 'result'.  Returns 0 if the codepoint was not found in the cache. */
+Uint8 FC_GetGlyphData(FC_Font* font, FC_GlyphData* result, Uint32 codepoint);
+
+/*! Sets the glyph data for the given codepoint.  Duplicates are not checked.  Returns a pointer to the stored data. */
+FC_GlyphData* FC_SetGlyphData(FC_Font* font, Uint32 codepoint, FC_GlyphData glyph_data);
+
+
+// Rendering
+
+FC_Rect FC_Draw(FC_Font* font, FC_Target* dest, float x, float y, const char* formatted_text, ...);
+FC_Rect FC_DrawAlign(FC_Font* font, FC_Target* dest, float x, float y, FC_AlignEnum align, const char* formatted_text, ...);
+FC_Rect FC_DrawScale(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* formatted_text, ...);
+FC_Rect FC_DrawColor(FC_Font* font, FC_Target* dest, float x, float y, SDL_Color color, const char* formatted_text, ...);
+FC_Rect FC_DrawEffect(FC_Font* font, FC_Target* dest, float x, float y, FC_Effect effect, const char* formatted_text, ...);
+
+FC_Rect FC_DrawBox(FC_Font* font, FC_Target* dest, FC_Rect box, const char* formatted_text, ...);
+FC_Rect FC_DrawBoxAlign(FC_Font* font, FC_Target* dest, FC_Rect box, FC_AlignEnum align, const char* formatted_text, ...);
+FC_Rect FC_DrawBoxScale(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Scale scale, const char* formatted_text, ...);
+FC_Rect FC_DrawBoxColor(FC_Font* font, FC_Target* dest, FC_Rect box, SDL_Color color, const char* formatted_text, ...);
+FC_Rect FC_DrawBoxEffect(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Effect effect, const char* formatted_text, ...);
+
+FC_Rect FC_DrawColumn(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, const char* formatted_text, ...);
+FC_Rect FC_DrawColumnAlign(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_AlignEnum align, const char* formatted_text, ...);
+FC_Rect FC_DrawColumnScale(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Scale scale, const char* formatted_text, ...);
+FC_Rect FC_DrawColumnColor(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, SDL_Color color, const char* formatted_text, ...);
+FC_Rect FC_DrawColumnEffect(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Effect effect, const char* formatted_text, ...);
+
+
+// Getters
+
+FC_FilterEnum FC_GetFilterMode(FC_Font* font);
+Uint16 FC_GetLineHeight(FC_Font* font);
+Uint16 FC_GetHeight(FC_Font* font, const char* formatted_text, ...);
+Uint16 FC_GetWidth(FC_Font* font, const char* formatted_text, ...);
+
+// Returns a 1-pixel wide box in front of the character in the given position (index)
+FC_Rect FC_GetCharacterOffset(FC_Font* font, Uint16 position_index, int column_width, const char* formatted_text, ...);
+Uint16 FC_GetColumnHeight(FC_Font* font, Uint16 width, const char* formatted_text, ...);
+
+int FC_GetAscent(FC_Font* font, const char* formatted_text, ...);
+int FC_GetDescent(FC_Font* font, const char* formatted_text, ...);
+int FC_GetBaseline(FC_Font* font);
+int FC_GetSpacing(FC_Font* font);
+int FC_GetLineSpacing(FC_Font* font);
+Uint16 FC_GetMaxWidth(FC_Font* font);
+SDL_Color FC_GetDefaultColor(FC_Font* font);
+
+FC_Rect FC_GetBounds(FC_Font* font, float x, float y, FC_AlignEnum align, FC_Scale scale, const char* formatted_text, ...);
+
+Uint8 FC_InRect(float x, float y, FC_Rect input_rect);
+// Given an offset (x,y) from the text draw position (the upper-left corner), returns the character position (UTF-8 index)
+Uint16 FC_GetPositionFromOffset(FC_Font* font, float x, float y, int column_width, FC_AlignEnum align, const char* formatted_text, ...);
+
+// Returns the number of characters in the new wrapped text written into `result`.
+int FC_GetWrappedText(FC_Font* font, char* result, int max_result_size, Uint16 width, const char* formatted_text, ...);
+
+// Setters
+
+void FC_SetFilterMode(FC_Font* font, FC_FilterEnum filter);
+void FC_SetSpacing(FC_Font* font, int LetterSpacing);
+void FC_SetLineSpacing(FC_Font* font, int LineSpacing);
+void FC_SetDefaultColor(FC_Font* font, SDL_Color color);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+#endif
diff --git a/include/app.hpp b/include/app.hpp
index 4e56f3cacdf00618755ffd6d72d1d924cc3eb923..90e1b2029fef62e201d48dbe431b05247ae1ed8b 100644
--- a/include/app.hpp
+++ b/include/app.hpp
@@ -51,6 +51,7 @@ namespace uwe {
         };
 
         // API
+
         int get_framebuffer_width() const {
             return context_.get_tex_width();
         }
@@ -67,15 +68,93 @@ namespace uwe {
             context_.set_pixel(offset, colour);
         }
 
+        void blit_framebuffer() {
+            // copy framebuffer pixels to GPU texture for rendering
+            SDL_UpdateTexture(
+                context_.get_texture(),
+                NULL,
+                context_.get_pixels().data(),
+                context_.get_tex_width() * 4);
+
+            SDL_RenderCopy( context_.get_renderer(), context_.get_texture(), NULL, NULL );
+        }
+
         void clear(Colour colour) {
             SDL_SetRenderDrawColor( 
                 context_.get_renderer(), 
-                colour.blue_,
-                colour.green_,
                 colour.red_,
+                colour.green_,
+                colour.blue_,
                 colour.alpha_ );
             SDL_RenderClear( context_.get_renderer() );
         }
+
+        Font create_font(std::string path, int point_size, Colour colour) {
+            return context_.create_font(path, point_size, colour);
+        }
+
+        void draw_font(Font font, std::string msg, float x, float y) {
+            context_.draw_font(font, msg, x, y);
+        }
+
+        Image create_image(std::string path) {
+            return context_.create_image(path);
+        }
+
+        Image create_image(int image_width, int image_height) {
+            return context_.create_image(image_width, image_height);
+        }
+
+        void get_image_size(Image image, int *width, int *height) {
+            context_.get_image_size(image, width, height);
+        }
+
+        void draw_image(Image image, Rect src, Rect dest) {
+            context_.draw_image(image, src, dest);
+        }
+
+        void set_renderer_taret(Image image, bool clear) {
+            SDL_SetRenderTarget(context_.get_renderer(), image);
+            if (clear) {
+                SDL_RenderClear(context_.get_renderer());
+            }
+        }
+
+        // drawing
+        void set_draw_color(Colour colour) {
+            SDL_SetRenderDrawColor( 
+                context_.get_renderer(), 
+                colour.red_,
+                colour.green_,
+                colour.blue_,
+                colour.alpha_ );
+        }
+
+        void draw_rect(int x, int y, int width, int height) {
+            Rect rect = make_rect(x, y, width, height);
+            SDL_RenderDrawRect(context_.get_renderer(), &rect);
+        }
+
+        void draw_rect(Rect rect) {
+            SDL_RenderDrawRect(context_.get_renderer(), &rect);
+        }
+
+        void draw_rect_fill(int x, int y, int width, int height) {
+            Rect rect = make_rect(x, y, width, height);
+            SDL_RenderFillRect(context_.get_renderer(), &rect);
+        }
+
+        void draw_rect_fill(Rect rect) {
+            SDL_RenderFillRect(context_.get_renderer(), &rect);
+        }
+
+        void draw_line(int x1, int y1, int x2, int y2) {
+            SDL_RenderDrawLine(context_.get_renderer(), x1, y1, x2, y2);
+        }
+
+        void draw_point(int x, int y) {
+            SDL_RenderDrawPoint(context_.get_renderer(), x, y);
+        }
     };
 
     void run();
diff --git a/include/context.hpp b/include/context.hpp
index 68afe6e92f8e66ad4b692707ee84b4712c9ee5a4..5eccc3794430b38f971bdf72971a8fa4e254c3bf 100644
--- a/include/context.hpp
+++ b/include/context.hpp
@@ -7,6 +7,8 @@
 #include <vector>
 
 #include <SDL2/SDL.h>
+#include <SDL2/SDL_image.h>
+#include <SDL_FontCache.h>
 
 #include <events.hpp>
 
@@ -46,6 +48,25 @@ namespace uwe {
         }
     };
 
+    inline Colour make_colour(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) {
+        return Colour{red, green, blue, alpha};
+    }
+
+    typedef SDL_Rect Rect;
+
+    inline Rect make_rect(int x, int y, int width, int height) {
+        Rect rect;
+        rect.x = x;
+        rect.y = y;
+        rect.w = width;
+        rect.h = height;
+        return rect;
+    }
+
+    typedef SDL_Texture* Image;
+
+    typedef FC_Font* Font;
+
     class Context {
     public:
         /** @brief Create a context
@@ -94,9 +115,9 @@ namespace uwe {
         }
 
         void set_pixel(size_t offset, Colour colour) {
-            pixels_[offset + 0] = colour.blue_;
+            pixels_[offset + 0] = colour.red_;
             pixels_[offset + 1] = colour.green_;
-            pixels_[offset + 2] = colour.red_;
+            pixels_[offset + 2] = colour.blue_;
             pixels_[offset + 3] = colour.alpha_;
         }
 
@@ -107,6 +128,53 @@ namespace uwe {
         int get_tex_height() const {
             return tex_height_;
         }
+
+        Image create_image(std::string path) {
+            SDL_Surface* surface = IMG_Load(path.c_str()); 
+            SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer_, surface); 
+            SDL_FreeSurface(surface);
+            return texture;
+        }
+
+        Image create_image(int tex_width, int tex_height) {
+            return SDL_CreateTexture(
+                renderer_, 
+                SDL_PIXELFORMAT_RGBA8888, 
+                SDL_TEXTUREACCESS_TARGET,
+                tex_width,
+                tex_height);
+        }
+
+        void delete_image(Image image) {
+            SDL_DestroyTexture(image);
+        }
+
+        void get_image_size(Image image, int *width, int *height) {
+            uint32_t format;
+            int access;
+            SDL_QueryTexture(image, &format, &access, width, height);
+        }
+
+        void draw_image(Image image, Rect src, Rect dest) {
+            SDL_RenderCopy(renderer_, image, &src, &dest);
+        }
+
+        Font create_font(std::string path, int point_size, Colour colour) {
+            auto font = FC_CreateFont();
+            FC_LoadFont(
+                font, 
+                renderer_, 
+                path.c_str(), 
+                point_size, 
+                FC_MakeColor(colour.red_, colour.green_, colour.blue_, colour.alpha_), 
+                TTF_STYLE_NORMAL);
+            loaded_fonts_.push_back(font);
+            return font;
+        }
+
+        void draw_font(Font font, std::string msg, float x, float y) {
+            FC_Draw(font, renderer_, x, y, msg.c_str()); 
+        }
     private:
         int height_;                ///< window height
         int width_;                 ///< window width
@@ -117,6 +185,7 @@ namespace uwe {
         SDL_Texture* texture_;      ///< pixel buffer texture handle
 
         std::vector< uint8_t > pixels_; ///< pixels representing framebuffer
+        std::vector<Font> loaded_fonts_;
     };
 }
 
diff --git a/libjpeg-9.dll b/libjpeg-9.dll
new file mode 100644
index 0000000000000000000000000000000000000000..9a05528eff3bc65d0f67b93f6d24dd82621a298a
Binary files /dev/null and b/libjpeg-9.dll differ
diff --git a/libpng16-16.dll b/libpng16-16.dll
new file mode 100644
index 0000000000000000000000000000000000000000..709f724459a3f036811cf7cfbac64f3c900ea739
Binary files /dev/null and b/libpng16-16.dll differ
diff --git a/libtiff-5.dll b/libtiff-5.dll
new file mode 100644
index 0000000000000000000000000000000000000000..fc8a7c0ca04ebc95ae2549d80f4516f771003731
Binary files /dev/null and b/libtiff-5.dll differ
diff --git a/libwebp-7.dll b/libwebp-7.dll
new file mode 100644
index 0000000000000000000000000000000000000000..fad57b229ea2d91c7767b2a6f10209baebf4f562
Binary files /dev/null and b/libwebp-7.dll differ
diff --git a/osx/include/SDL2/SDL_image.h b/osx/include/SDL2/SDL_image.h
new file mode 100644
index 0000000000000000000000000000000000000000..6facc0d52c6c4337b4b5a551aa5a96ac427bad1b
--- /dev/null
+++ b/osx/include/SDL2/SDL_image.h
@@ -0,0 +1,161 @@
+/*
+  SDL_image:  An example image loading library for use with SDL
+  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+/* A simple library to load images of various formats as SDL surfaces */
+
+#ifndef SDL_IMAGE_H_
+#define SDL_IMAGE_H_
+
+#include "SDL.h"
+#include "SDL_version.h"
+#include "begin_code.h"
+
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL
+*/
+#define SDL_IMAGE_MAJOR_VERSION 2
+#define SDL_IMAGE_MINOR_VERSION 0
+#define SDL_IMAGE_PATCHLEVEL    5
+
+/* This macro can be used to fill a version structure with the compile-time
+ * version of the SDL_image library.
+ */
+#define SDL_IMAGE_VERSION(X)                        \
+{                                                   \
+    (X)->major = SDL_IMAGE_MAJOR_VERSION;           \
+    (X)->minor = SDL_IMAGE_MINOR_VERSION;           \
+    (X)->patch = SDL_IMAGE_PATCHLEVEL;              \
+}
+
+/**
+ *  This is the version number macro for the current SDL_image version.
+ */
+#define SDL_IMAGE_COMPILEDVERSION \
+    SDL_VERSIONNUM(SDL_IMAGE_MAJOR_VERSION, SDL_IMAGE_MINOR_VERSION, SDL_IMAGE_PATCHLEVEL)
+
+/**
+ *  This macro will evaluate to true if compiled with SDL_image at least X.Y.Z.
+ */
+#define SDL_IMAGE_VERSION_ATLEAST(X, Y, Z) \
+    (SDL_IMAGE_COMPILEDVERSION >= SDL_VERSIONNUM(X, Y, Z))
+
+/* This function gets the version of the dynamically linked SDL_image library.
+   it should NOT be used to fill a version structure, instead you should
+   use the SDL_IMAGE_VERSION() macro.
+ */
+extern DECLSPEC const SDL_version * SDLCALL IMG_Linked_Version(void);
+
+typedef enum
+{
+    IMG_INIT_JPG = 0x00000001,
+    IMG_INIT_PNG = 0x00000002,
+    IMG_INIT_TIF = 0x00000004,
+    IMG_INIT_WEBP = 0x00000008
+} IMG_InitFlags;
+
+/* Loads dynamic libraries and prepares them for use.  Flags should be
+   one or more flags from IMG_InitFlags OR'd together.
+   It returns the flags successfully initialized, or 0 on failure.
+ */
+extern DECLSPEC int SDLCALL IMG_Init(int flags);
+
+/* Unloads libraries loaded with IMG_Init */
+extern DECLSPEC void SDLCALL IMG_Quit(void);
+
+/* Load an image from an SDL data source.
+   The 'type' may be one of: "BMP", "GIF", "PNG", etc.
+
+   If the image format supports a transparent pixel, SDL will set the
+   colorkey for the surface.  You can enable RLE acceleration on the
+   surface afterwards by calling:
+    SDL_SetColorKey(image, SDL_RLEACCEL, image->format->colorkey);
+ */
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadTyped_RW(SDL_RWops *src, int freesrc, const char *type);
+/* Convenience functions */
+extern DECLSPEC SDL_Surface * SDLCALL IMG_Load(const char *file);
+extern DECLSPEC SDL_Surface * SDLCALL IMG_Load_RW(SDL_RWops *src, int freesrc);
+
+#if SDL_VERSION_ATLEAST(2,0,0)
+/* Load an image directly into a render texture.
+ */
+extern DECLSPEC SDL_Texture * SDLCALL IMG_LoadTexture(SDL_Renderer *renderer, const char *file);
+extern DECLSPEC SDL_Texture * SDLCALL IMG_LoadTexture_RW(SDL_Renderer *renderer, SDL_RWops *src, int freesrc);
+extern DECLSPEC SDL_Texture * SDLCALL IMG_LoadTextureTyped_RW(SDL_Renderer *renderer, SDL_RWops *src, int freesrc, const char *type);
+#endif /* SDL 2.0 */
+
+/* Functions to detect a file type, given a seekable source */
+extern DECLSPEC int SDLCALL IMG_isICO(SDL_RWops *src);
+extern DECLSPEC int SDLCALL IMG_isCUR(SDL_RWops *src);
+extern DECLSPEC int SDLCALL IMG_isBMP(SDL_RWops *src);
+extern DECLSPEC int SDLCALL IMG_isGIF(SDL_RWops *src);
+extern DECLSPEC int SDLCALL IMG_isJPG(SDL_RWops *src);
+extern DECLSPEC int SDLCALL IMG_isLBM(SDL_RWops *src);
+extern DECLSPEC int SDLCALL IMG_isPCX(SDL_RWops *src);
+extern DECLSPEC int SDLCALL IMG_isPNG(SDL_RWops *src);
+extern DECLSPEC int SDLCALL IMG_isPNM(SDL_RWops *src);
+extern DECLSPEC int SDLCALL IMG_isSVG(SDL_RWops *src);
+extern DECLSPEC int SDLCALL IMG_isTIF(SDL_RWops *src);
+extern DECLSPEC int SDLCALL IMG_isXCF(SDL_RWops *src);
+extern DECLSPEC int SDLCALL IMG_isXPM(SDL_RWops *src);
+extern DECLSPEC int SDLCALL IMG_isXV(SDL_RWops *src);
+extern DECLSPEC int SDLCALL IMG_isWEBP(SDL_RWops *src);
+
+/* Individual loading functions */
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadICO_RW(SDL_RWops *src);
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadCUR_RW(SDL_RWops *src);
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadBMP_RW(SDL_RWops *src);
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadGIF_RW(SDL_RWops *src);
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadJPG_RW(SDL_RWops *src);
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadLBM_RW(SDL_RWops *src);
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadPCX_RW(SDL_RWops *src);
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadPNG_RW(SDL_RWops *src);
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadPNM_RW(SDL_RWops *src);
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadSVG_RW(SDL_RWops *src);
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadTGA_RW(SDL_RWops *src);
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadTIF_RW(SDL_RWops *src);
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadXCF_RW(SDL_RWops *src);
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadXPM_RW(SDL_RWops *src);
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadXV_RW(SDL_RWops *src);
+extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadWEBP_RW(SDL_RWops *src);
+
+extern DECLSPEC SDL_Surface * SDLCALL IMG_ReadXPMFromArray(char **xpm);
+
+/* Individual saving functions */
+extern DECLSPEC int SDLCALL IMG_SavePNG(SDL_Surface *surface, const char *file);
+extern DECLSPEC int SDLCALL IMG_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst);
+extern DECLSPEC int SDLCALL IMG_SaveJPG(SDL_Surface *surface, const char *file, int quality);
+extern DECLSPEC int SDLCALL IMG_SaveJPG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst, int quality);
+
+/* We'll use SDL for reporting errors */
+#define IMG_SetError    SDL_SetError
+#define IMG_GetError    SDL_GetError
+
+/* Ends C function definitions when using C++ */
+#ifdef __cplusplus
+}
+#endif
+#include "close_code.h"
+
+#endif /* SDL_IMAGE_H_ */
diff --git a/osx/include/SDL2/SDL_ttf.h b/osx/include/SDL2/SDL_ttf.h
new file mode 100644
index 0000000000000000000000000000000000000000..08611857cd5c40db63c4ecb54de9cc4bee7282a1
--- /dev/null
+++ b/osx/include/SDL2/SDL_ttf.h
@@ -0,0 +1,294 @@
+/*
+  SDL_ttf:  A companion library to SDL for working with TrueType (tm) fonts
+  Copyright (C) 2001-2019 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+/* This library is a wrapper around the excellent FreeType 2.0 library,
+   available at:
+    http://www.freetype.org/
+*/
+
+/* Note: In many places, SDL_ttf will say "glyph" when it means "code point."
+   Unicode is hard, we learn as we go, and we apologize for adding to the
+   confusion. */
+
+#ifndef SDL_TTF_H_
+#define SDL_TTF_H_
+
+#include "SDL.h"
+#include "begin_code.h"
+
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL
+*/
+#define SDL_TTF_MAJOR_VERSION   2
+#define SDL_TTF_MINOR_VERSION   0
+#define SDL_TTF_PATCHLEVEL      15
+
+/* This macro can be used to fill a version structure with the compile-time
+ * version of the SDL_ttf library.
+ */
+#define SDL_TTF_VERSION(X)                          \
+{                                                   \
+    (X)->major = SDL_TTF_MAJOR_VERSION;             \
+    (X)->minor = SDL_TTF_MINOR_VERSION;             \
+    (X)->patch = SDL_TTF_PATCHLEVEL;                \
+}
+
+/* Backwards compatibility */
+#define TTF_MAJOR_VERSION   SDL_TTF_MAJOR_VERSION
+#define TTF_MINOR_VERSION   SDL_TTF_MINOR_VERSION
+#define TTF_PATCHLEVEL      SDL_TTF_PATCHLEVEL
+#define TTF_VERSION(X)      SDL_TTF_VERSION(X)
+
+/**
+ *  This is the version number macro for the current SDL_ttf version.
+ */
+#define SDL_TTF_COMPILEDVERSION \
+    SDL_VERSIONNUM(SDL_TTF_MAJOR_VERSION, SDL_TTF_MINOR_VERSION, SDL_TTF_PATCHLEVEL)
+
+/**
+ *  This macro will evaluate to true if compiled with SDL_ttf at least X.Y.Z.
+ */
+#define SDL_TTF_VERSION_ATLEAST(X, Y, Z) \
+    (SDL_TTF_COMPILEDVERSION >= SDL_VERSIONNUM(X, Y, Z))
+
+/* Make sure this is defined (only available in newer SDL versions) */
+#ifndef SDL_DEPRECATED
+#define SDL_DEPRECATED
+#endif
+
+/* This function gets the version of the dynamically linked SDL_ttf library.
+   it should NOT be used to fill a version structure, instead you should
+   use the SDL_TTF_VERSION() macro.
+ */
+extern DECLSPEC const SDL_version * SDLCALL TTF_Linked_Version(void);
+
+/* ZERO WIDTH NO-BREAKSPACE (Unicode byte order mark) */
+#define UNICODE_BOM_NATIVE  0xFEFF
+#define UNICODE_BOM_SWAPPED 0xFFFE
+
+/* This function tells the library whether UNICODE text is generally
+   byteswapped.  A UNICODE BOM character in a string will override
+   this setting for the remainder of that string.
+*/
+extern DECLSPEC void SDLCALL TTF_ByteSwappedUNICODE(int swapped);
+
+/* The internal structure containing font information */
+typedef struct _TTF_Font TTF_Font;
+
+/* Initialize the TTF engine - returns 0 if successful, -1 on error */
+extern DECLSPEC int SDLCALL TTF_Init(void);
+
+/* Open a font file and create a font of the specified point size.
+ * Some .fon fonts will have several sizes embedded in the file, so the
+ * point size becomes the index of choosing which size.  If the value
+ * is too high, the last indexed size will be the default. */
+extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFont(const char *file, int ptsize);
+extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndex(const char *file, int ptsize, long index);
+extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontRW(SDL_RWops *src, int freesrc, int ptsize);
+extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndexRW(SDL_RWops *src, int freesrc, int ptsize, long index);
+
+/* Set and retrieve the font style */
+#define TTF_STYLE_NORMAL        0x00
+#define TTF_STYLE_BOLD          0x01
+#define TTF_STYLE_ITALIC        0x02
+#define TTF_STYLE_UNDERLINE     0x04
+#define TTF_STYLE_STRIKETHROUGH 0x08
+extern DECLSPEC int SDLCALL TTF_GetFontStyle(const TTF_Font *font);
+extern DECLSPEC void SDLCALL TTF_SetFontStyle(TTF_Font *font, int style);
+extern DECLSPEC int SDLCALL TTF_GetFontOutline(const TTF_Font *font);
+extern DECLSPEC void SDLCALL TTF_SetFontOutline(TTF_Font *font, int outline);
+
+/* Set and retrieve FreeType hinter settings */
+#define TTF_HINTING_NORMAL    0
+#define TTF_HINTING_LIGHT     1
+#define TTF_HINTING_MONO      2
+#define TTF_HINTING_NONE      3
+extern DECLSPEC int SDLCALL TTF_GetFontHinting(const TTF_Font *font);
+extern DECLSPEC void SDLCALL TTF_SetFontHinting(TTF_Font *font, int hinting);
+
+/* Get the total height of the font - usually equal to point size */
+extern DECLSPEC int SDLCALL TTF_FontHeight(const TTF_Font *font);
+
+/* Get the offset from the baseline to the top of the font
+   This is a positive value, relative to the baseline.
+ */
+extern DECLSPEC int SDLCALL TTF_FontAscent(const TTF_Font *font);
+
+/* Get the offset from the baseline to the bottom of the font
+   This is a negative value, relative to the baseline.
+ */
+extern DECLSPEC int SDLCALL TTF_FontDescent(const TTF_Font *font);
+
+/* Get the recommended spacing between lines of text for this font */
+extern DECLSPEC int SDLCALL TTF_FontLineSkip(const TTF_Font *font);
+
+/* Get/Set whether or not kerning is allowed for this font */
+extern DECLSPEC int SDLCALL TTF_GetFontKerning(const TTF_Font *font);
+extern DECLSPEC void SDLCALL TTF_SetFontKerning(TTF_Font *font, int allowed);
+
+/* Get the number of faces of the font */
+extern DECLSPEC long SDLCALL TTF_FontFaces(const TTF_Font *font);
+
+/* Get the font face attributes, if any */
+extern DECLSPEC int SDLCALL TTF_FontFaceIsFixedWidth(const TTF_Font *font);
+extern DECLSPEC char * SDLCALL TTF_FontFaceFamilyName(const TTF_Font *font);
+extern DECLSPEC char * SDLCALL TTF_FontFaceStyleName(const TTF_Font *font);
+
+/* Check wether a glyph is provided by the font or not */
+extern DECLSPEC int SDLCALL TTF_GlyphIsProvided(const TTF_Font *font, Uint16 ch);
+
+/* Get the metrics (dimensions) of a glyph
+   To understand what these metrics mean, here is a useful link:
+    http://freetype.sourceforge.net/freetype2/docs/tutorial/step2.html
+ */
+extern DECLSPEC int SDLCALL TTF_GlyphMetrics(TTF_Font *font, Uint16 ch,
+                     int *minx, int *maxx,
+                                     int *miny, int *maxy, int *advance);
+
+/* Get the dimensions of a rendered string of text */
+extern DECLSPEC int SDLCALL TTF_SizeText(TTF_Font *font, const char *text, int *w, int *h);
+extern DECLSPEC int SDLCALL TTF_SizeUTF8(TTF_Font *font, const char *text, int *w, int *h);
+extern DECLSPEC int SDLCALL TTF_SizeUNICODE(TTF_Font *font, const Uint16 *text, int *w, int *h);
+
+/* Create an 8-bit palettized surface and render the given text at
+   fast quality with the given font and color.  The 0 pixel is the
+   colorkey, giving a transparent background, and the 1 pixel is set
+   to the text color.
+   This function returns the new surface, or NULL if there was an error.
+*/
+extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Solid(TTF_Font *font,
+                const char *text, SDL_Color fg);
+extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Solid(TTF_Font *font,
+                const char *text, SDL_Color fg);
+extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Solid(TTF_Font *font,
+                const Uint16 *text, SDL_Color fg);
+
+/* Create an 8-bit palettized surface and render the given glyph at
+   fast quality with the given font and color.  The 0 pixel is the
+   colorkey, giving a transparent background, and the 1 pixel is set
+   to the text color.  The glyph is rendered without any padding or
+   centering in the X direction, and aligned normally in the Y direction.
+   This function returns the new surface, or NULL if there was an error.
+*/
+extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Solid(TTF_Font *font,
+                    Uint16 ch, SDL_Color fg);
+
+/* Create an 8-bit palettized surface and render the given text at
+   high quality with the given font and colors.  The 0 pixel is background,
+   while other pixels have varying degrees of the foreground color.
+   This function returns the new surface, or NULL if there was an error.
+*/
+extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded(TTF_Font *font,
+                const char *text, SDL_Color fg, SDL_Color bg);
+extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Shaded(TTF_Font *font,
+                const char *text, SDL_Color fg, SDL_Color bg);
+extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Shaded(TTF_Font *font,
+                const Uint16 *text, SDL_Color fg, SDL_Color bg);
+
+/* Create an 8-bit palettized surface and render the given glyph at
+   high quality with the given font and colors.  The 0 pixel is background,
+   while other pixels have varying degrees of the foreground color.
+   The glyph is rendered without any padding or centering in the X
+   direction, and aligned normally in the Y direction.
+   This function returns the new surface, or NULL if there was an error.
+*/
+extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Shaded(TTF_Font *font,
+                Uint16 ch, SDL_Color fg, SDL_Color bg);
+
+/* Create a 32-bit ARGB surface and render the given text at high quality,
+   using alpha blending to dither the font with the given color.
+   This function returns the new surface, or NULL if there was an error.
+*/
+extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended(TTF_Font *font,
+                const char *text, SDL_Color fg);
+extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Blended(TTF_Font *font,
+                const char *text, SDL_Color fg);
+extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Blended(TTF_Font *font,
+                const Uint16 *text, SDL_Color fg);
+
+
+/* Create a 32-bit ARGB surface and render the given text at high quality,
+   using alpha blending to dither the font with the given color.
+   Text is wrapped to multiple lines on line endings and on word boundaries
+   if it extends beyond wrapLength in pixels.
+   This function returns the new surface, or NULL if there was an error.
+*/
+extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended_Wrapped(TTF_Font *font,
+                const char *text, SDL_Color fg, Uint32 wrapLength);
+extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Blended_Wrapped(TTF_Font *font,
+                const char *text, SDL_Color fg, Uint32 wrapLength);
+extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Blended_Wrapped(TTF_Font *font,
+                const Uint16 *text, SDL_Color fg, Uint32 wrapLength);
+
+/* Create a 32-bit ARGB surface and render the given glyph at high quality,
+   using alpha blending to dither the font with the given color.
+   The glyph is rendered without any padding or centering in the X
+   direction, and aligned normally in the Y direction.
+   This function returns the new surface, or NULL if there was an error.
+*/
+extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Blended(TTF_Font *font,
+                        Uint16 ch, SDL_Color fg);
+
+/* For compatibility with previous versions, here are the old functions */
+#define TTF_RenderText(font, text, fg, bg)  \
+    TTF_RenderText_Shaded(font, text, fg, bg)
+#define TTF_RenderUTF8(font, text, fg, bg)  \
+    TTF_RenderUTF8_Shaded(font, text, fg, bg)
+#define TTF_RenderUNICODE(font, text, fg, bg)   \
+    TTF_RenderUNICODE_Shaded(font, text, fg, bg)
+
+/* Close an opened font file */
+extern DECLSPEC void SDLCALL TTF_CloseFont(TTF_Font *font);
+
+/* De-initialize the TTF engine */
+extern DECLSPEC void SDLCALL TTF_Quit(void);
+
+/* Check if the TTF engine is initialized */
+extern DECLSPEC int SDLCALL TTF_WasInit(void);
+
+/* Get the kerning size of two glyphs indices */
+/* DEPRECATED: this function requires FreeType font indexes, not glyphs,
+   by accident, which we don't expose through this API, so it could give
+   wildly incorrect results, especially with non-ASCII values.
+   Going forward, please use TTF_GetFontKerningSizeGlyphs() instead, which
+   does what you probably expected this function to do. */
+extern DECLSPEC int TTF_GetFontKerningSize(TTF_Font *font, int prev_index, int index) SDL_DEPRECATED;
+
+/* Get the kerning size of two glyphs */
+extern DECLSPEC int TTF_GetFontKerningSizeGlyphs(TTF_Font *font, Uint16 previous_ch, Uint16 ch);
+
+/* We'll use SDL for reporting errors */
+#define TTF_SetError    SDL_SetError
+#define TTF_GetError    SDL_GetError
+
+/* Ends C function definitions when using C++ */
+#ifdef __cplusplus
+}
+#endif
+#include "close_code.h"
+
+#endif /* SDL_TTF_H_ */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/osx/lib/libSDL2_image.a b/osx/lib/libSDL2_image.a
new file mode 100644
index 0000000000000000000000000000000000000000..d628bf8b17d81633e8c98d23db356a00919aede2
Binary files /dev/null and b/osx/lib/libSDL2_image.a differ
diff --git a/osx/lib/libSDL2_ttf.a b/osx/lib/libSDL2_ttf.a
new file mode 100644
index 0000000000000000000000000000000000000000..415960e087fcd270c1575710c1ee72dfd3db553b
Binary files /dev/null and b/osx/lib/libSDL2_ttf.a differ
diff --git a/osx/lib/libbz2.a b/osx/lib/libbz2.a
new file mode 100644
index 0000000000000000000000000000000000000000..0dabcb61fdc697996424b20e9374cb8dd68420d2
Binary files /dev/null and b/osx/lib/libbz2.a differ
diff --git a/osx/lib/libfreetype.a b/osx/lib/libfreetype.a
new file mode 100644
index 0000000000000000000000000000000000000000..0b65ed75f0a4fc3a3918a79d8e862e695cf329c2
Binary files /dev/null and b/osx/lib/libfreetype.a differ
diff --git a/osx/lib/libjpeg.a b/osx/lib/libjpeg.a
new file mode 100644
index 0000000000000000000000000000000000000000..40600ff589ddc52838a482884a6fc67ab4a4a1a9
Binary files /dev/null and b/osx/lib/libjpeg.a differ
diff --git a/osx/lib/libpng.a b/osx/lib/libpng.a
new file mode 100644
index 0000000000000000000000000000000000000000..9dc7ec531812f5fa3957c00c2f63211f56366d2e
Binary files /dev/null and b/osx/lib/libpng.a differ
diff --git a/osx/lib/libpng16.a b/osx/lib/libpng16.a
new file mode 100644
index 0000000000000000000000000000000000000000..9dc7ec531812f5fa3957c00c2f63211f56366d2e
Binary files /dev/null and b/osx/lib/libpng16.a differ
diff --git a/osx/lib/libtiff.a b/osx/lib/libtiff.a
new file mode 100644
index 0000000000000000000000000000000000000000..330047be72f98c44f88609d7c788d6a9c0934382
Binary files /dev/null and b/osx/lib/libtiff.a differ
diff --git a/osx/lib/libtiffxx.a b/osx/lib/libtiffxx.a
new file mode 100644
index 0000000000000000000000000000000000000000..982e5f301b6b1eaa1c33b8e5b5ccfbbd3e5335da
Binary files /dev/null and b/osx/lib/libtiffxx.a differ
diff --git a/osx/lib/libwebp.a b/osx/lib/libwebp.a
new file mode 100644
index 0000000000000000000000000000000000000000..b31b2550ba4d27e5f16844880ac6b31bdd3be00f
Binary files /dev/null and b/osx/lib/libwebp.a differ
diff --git a/osx/lib/libwebpdecoder.a b/osx/lib/libwebpdecoder.a
new file mode 100644
index 0000000000000000000000000000000000000000..2b3eb9a6febcbd774cdd7333212e283eee55f427
Binary files /dev/null and b/osx/lib/libwebpdecoder.a differ
diff --git a/osx/lib/libwebpdemux.a b/osx/lib/libwebpdemux.a
new file mode 100644
index 0000000000000000000000000000000000000000..d84725d65fbf9a742815298e80968915e0482765
Binary files /dev/null and b/osx/lib/libwebpdemux.a differ
diff --git a/osx/lib/libwebpmux.a b/osx/lib/libwebpmux.a
new file mode 100644
index 0000000000000000000000000000000000000000..868d2742a47f2e9131a34fbb461110b60f0bc516
Binary files /dev/null and b/osx/lib/libwebpmux.a differ
diff --git a/osx/lib/libz.a b/osx/lib/libz.a
new file mode 100644
index 0000000000000000000000000000000000000000..8dfb376f220e358e066659ffbb6fec8acd787d3a
Binary files /dev/null and b/osx/lib/libz.a differ
diff --git a/src/SDL_FontCache.c b/src/SDL_FontCache.c
new file mode 100644
index 0000000000000000000000000000000000000000..e534622d6e55e1a24c266e6f0c2362645784c965
--- /dev/null
+++ b/src/SDL_FontCache.c
@@ -0,0 +1,2925 @@
+/*
+SDL_FontCache: A font cache for SDL and SDL_ttf
+by Jonathan Dearborn
+
+See SDL_FontCache.h for license info.
+*/
+
+#include "SDL_FontCache.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// Visual C does not support static inline
+#ifndef static_inline
+	#ifdef _MSC_VER
+		#define static_inline static
+	#else
+		#define static_inline static inline
+	#endif
+#endif
+
+#if SDL_VERSION_ATLEAST(2,0,0)
+    #define FC_GET_ALPHA(sdl_color) ((sdl_color).a)
+#else
+    #define FC_GET_ALPHA(sdl_color) ((sdl_color).unused)
+#endif
+
+// Need SDL_RenderIsClipEnabled() for proper clipping support
+#if SDL_VERSION_ATLEAST(2,0,4)
+    #define ENABLE_SDL_CLIPPING
+#endif
+
+#define FC_MIN(a,b) ((a) < (b)? (a) : (b))
+#define FC_MAX(a,b) ((a) > (b)? (a) : (b))
+
+
+// vsnprintf replacement from Valentin Milea:
+// http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
+#if defined(_MSC_VER) && _MSC_VER < 1900
+
+#define snprintf c99_snprintf
+#define vsnprintf c99_vsnprintf
+
+__inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
+{
+    int count = -1;
+
+    if (size != 0)
+        count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
+    if (count == -1)
+        count = _vscprintf(format, ap);
+
+    return count;
+}
+
+__inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...)
+{
+    int count;
+    va_list ap;
+
+    va_start(ap, format);
+    count = c99_vsnprintf(outBuf, size, format, ap);
+    va_end(ap);
+
+    return count;
+}
+
+#endif
+
+
+#define FC_EXTRACT_VARARGS(buffer, start_args) \
+{ \
+    va_list lst; \
+    va_start(lst, start_args); \
+    vsnprintf(buffer, fc_buffer_size, start_args, lst); \
+    va_end(lst); \
+}
+
+// Extra pixels of padding around each glyph to avoid linear filtering artifacts
+#define FC_CACHE_PADDING 1
+
+
+
+static Uint8 has_clip(FC_Target* dest)
+{
+    #ifdef FC_USE_SDL_GPU
+    return dest->use_clip_rect;
+    #elif defined(ENABLE_SDL_CLIPPING)
+    return SDL_RenderIsClipEnabled(dest);
+    #else
+    return 0;
+    #endif
+}
+
+static FC_Rect get_clip(FC_Target* dest)
+{
+    #ifdef FC_USE_SDL_GPU
+    return dest->clip_rect;
+    #elif defined(ENABLE_SDL_CLIPPING)
+    SDL_Rect r;
+    SDL_RenderGetClipRect(dest, &r);
+    return r;
+    #else
+    SDL_Rect r = {0, 0, 0, 0};
+    return r;
+    #endif
+}
+
+static void set_clip(FC_Target* dest, FC_Rect* rect)
+{
+    #ifdef FC_USE_SDL_GPU
+    if(rect != NULL)
+        GPU_SetClipRect(dest, *rect);
+    else
+        GPU_UnsetClip(dest);
+    #elif defined(ENABLE_SDL_CLIPPING)
+    SDL_RenderSetClipRect(dest, rect);
+    #endif
+}
+
+static void set_color(FC_Image* src, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
+{
+    #ifdef FC_USE_SDL_GPU
+    GPU_SetRGBA(src, r, g, b, a);
+    #else
+    SDL_SetTextureColorMod(src, r, g, b);
+    SDL_SetTextureAlphaMod(src, a);
+    #endif
+}
+
+
+
+static char* new_concat(const char* a, const char* b)
+{
+    // Create new buffer
+    unsigned int size = strlen(a) + strlen(b);
+    char* new_string = (char*)malloc(size+1);
+
+    // Concatenate strings in the new buffer
+    strcpy(new_string, a);
+    strcat(new_string, b);
+
+    return new_string;
+}
+
+static char* replace_concat(char** a, const char* b)
+{
+    char* new_string = new_concat(*a, b);
+    free(*a);
+    *a = new_string;
+    return *a;
+}
+
+
+// Width of a tab in units of the space width (sorry, no tab alignment!)
+static unsigned int fc_tab_width = 4;
+
+// Shared buffer for variadic text
+static char* fc_buffer = NULL;
+static unsigned int fc_buffer_size = 1024;
+
+static Uint8 fc_has_render_target_support = 0;
+
+// The number of fonts that has been created but not freed
+static int NUM_EXISTING_FONTS = 0;
+
+// Globals for GetString functions
+static char* ASCII_STRING = NULL;
+static char* LATIN_1_STRING = NULL;
+static char* ASCII_LATIN_1_STRING = NULL;
+
+char* FC_GetStringASCII(void)
+{
+    if(ASCII_STRING == NULL)
+    {
+        int i;
+        char c;
+        ASCII_STRING = (char*)malloc(512);
+        memset(ASCII_STRING, 0, 512);
+        i = 0;
+        c = 32;
+        while(1)
+        {
+            ASCII_STRING[i] = c;
+            if(c == 126)
+                break;
+            ++i;
+            ++c;
+        }
+    }
+    return U8_strdup(ASCII_STRING);
+}
+
+char* FC_GetStringLatin1(void)
+{
+    if(LATIN_1_STRING == NULL)
+    {
+        int i;
+        unsigned char c;
+        LATIN_1_STRING = (char*)malloc(512);
+        memset(LATIN_1_STRING, 0, 512);
+        i = 0;
+        c = 0xA0;
+        while(1)
+        {
+            LATIN_1_STRING[i] = 0xC2;
+            LATIN_1_STRING[i+1] = c;
+            if(c == 0xBF)
+                break;
+            i += 2;
+            ++c;
+        }
+        i += 2;
+        c = 0x80;
+        while(1)
+        {
+            LATIN_1_STRING[i] = 0xC3;
+            LATIN_1_STRING[i+1] = c;
+            if(c == 0xBF)
+                break;
+            i += 2;
+            ++c;
+        }
+    }
+    return U8_strdup(LATIN_1_STRING);
+}
+
+char* FC_GetStringASCII_Latin1(void)
+{
+    if(ASCII_LATIN_1_STRING == NULL)
+		ASCII_LATIN_1_STRING = new_concat(FC_GetStringASCII(), FC_GetStringLatin1());
+
+    return U8_strdup(ASCII_LATIN_1_STRING);
+}
+
+FC_Rect FC_MakeRect(float x, float y, float w, float h)
+{
+    FC_Rect r = {x, y, w, h};
+    return r;
+}
+
+FC_Scale FC_MakeScale(float x, float y)
+{
+    FC_Scale s = {x, y};
+
+    return s;
+}
+
+SDL_Color FC_MakeColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a)
+{
+    SDL_Color c = {r, g, b, a};
+
+    return c;
+}
+
+FC_Effect FC_MakeEffect(FC_AlignEnum alignment, FC_Scale scale, SDL_Color color)
+{
+    FC_Effect e;
+
+    e.alignment = alignment;
+    e.scale = scale;
+    e.color = color;
+
+    return e;
+}
+
+FC_GlyphData FC_MakeGlyphData(int cache_level, Sint16 x, Sint16 y, Uint16 w, Uint16 h)
+{
+    FC_GlyphData gd;
+
+    gd.rect.x = x;
+    gd.rect.y = y;
+    gd.rect.w = w;
+    gd.rect.h = h;
+    gd.cache_level = cache_level;
+
+    return gd;
+}
+
+// Enough to hold all of the ascii characters and some.
+#define FC_DEFAULT_NUM_BUCKETS 300
+
+typedef struct FC_MapNode
+{
+    Uint32 key;
+    FC_GlyphData value;
+    struct FC_MapNode* next;
+
+} FC_MapNode;
+
+typedef struct FC_Map
+{
+    int num_buckets;
+    FC_MapNode** buckets;
+} FC_Map;
+
+
+
+static FC_Map* FC_MapCreate(int num_buckets)
+{
+    int i;
+    FC_Map* map = (FC_Map*)malloc(sizeof(FC_Map));
+
+    map->num_buckets = num_buckets;
+    map->buckets = (FC_MapNode**)malloc(num_buckets * sizeof(FC_MapNode*));
+
+    for(i = 0; i < num_buckets; ++i)
+    {
+        map->buckets[i] = NULL;
+    }
+
+    return map;
+}
+
+/*static void FC_MapClear(FC_Map* map)
+{
+    int i;
+    if(map == NULL)
+        return;
+
+    // Go through each bucket
+    for(i = 0; i < map->num_buckets; ++i)
+    {
+        // Delete the nodes in order
+        FC_MapNode* node = map->buckets[i];
+        while(node != NULL)
+        {
+            FC_MapNode* last = node;
+            node = node->next;
+            free(last);
+        }
+        // Set the bucket to empty
+        map->buckets[i] = NULL;
+    }
+}*/
+
+static void FC_MapFree(FC_Map* map)
+{
+    int i;
+    if(map == NULL)
+        return;
+
+    // Go through each bucket
+    for(i = 0; i < map->num_buckets; ++i)
+    {
+        // Delete the nodes in order
+        FC_MapNode* node = map->buckets[i];
+        while(node != NULL)
+        {
+            FC_MapNode* last = node;
+            node = node->next;
+            free(last);
+        }
+    }
+
+    free(map->buckets);
+    free(map);
+}
+
+// Note: Does not handle duplicates in any special way.
+static FC_GlyphData* FC_MapInsert(FC_Map* map, Uint32 codepoint, FC_GlyphData glyph)
+{
+    Uint32 index;
+    FC_MapNode* node;
+    if(map == NULL)
+        return NULL;
+
+    // Get index for bucket
+    index = codepoint % map->num_buckets;
+
+    // If this bucket is empty, create a node and return its value
+    if(map->buckets[index] == NULL)
+    {
+        node = map->buckets[index] = (FC_MapNode*)malloc(sizeof(FC_MapNode));
+        node->key = codepoint;
+        node->value = glyph;
+        node->next = NULL;
+        return &node->value;
+    }
+
+    for(node = map->buckets[index]; node != NULL; node = node->next)
+    {
+        // Find empty node and add a new one on.
+        if(node->next == NULL)
+        {
+            node->next = (FC_MapNode*)malloc(sizeof(FC_MapNode));
+            node = node->next;
+
+            node->key = codepoint;
+            node->value = glyph;
+            node->next = NULL;
+            return &node->value;
+        }
+    }
+
+    return NULL;
+}
+
+static FC_GlyphData* FC_MapFind(FC_Map* map, Uint32 codepoint)
+{
+    Uint32 index;
+    FC_MapNode* node;
+    if(map == NULL)
+        return NULL;
+
+    // Get index for bucket
+    index = codepoint % map->num_buckets;
+
+    // Go through list until we find a match
+    for(node = map->buckets[index]; node != NULL; node = node->next)
+    {
+        if(node->key == codepoint)
+            return &node->value;
+    }
+
+    return NULL;
+}
+
+
+
+struct FC_Font
+{
+    #ifndef FC_USE_SDL_GPU
+    SDL_Renderer* renderer;
+    #endif
+
+    TTF_Font* ttf_source;  // TTF_Font source of characters
+    Uint8 owns_ttf_source;  // Can we delete the TTF_Font ourselves?
+
+    FC_FilterEnum filter;
+
+    SDL_Color default_color;
+    Uint16 height;
+
+    Uint16 maxWidth;
+    Uint16 baseline;
+    int ascent;
+    int descent;
+
+    int lineSpacing;
+    int letterSpacing;
+
+    // Uses 32-bit (4-byte) Unicode codepoints to refer to each glyph
+    // Codepoints are little endian (reversed from UTF-8) so that something like 0x00000005 is ASCII 5 and the map can be indexed by ASCII values
+    FC_Map* glyphs;
+
+    FC_GlyphData last_glyph;  // Texture packing cursor
+    int glyph_cache_size;
+    int glyph_cache_count;
+    FC_Image** glyph_cache;
+
+    char* loading_string;
+
+};
+
+// Private
+static FC_GlyphData* FC_PackGlyphData(FC_Font* font, Uint32 codepoint, Uint16 width, Uint16 maxWidth, Uint16 maxHeight);
+
+
+static FC_Rect FC_RenderLeft(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text);
+static FC_Rect FC_RenderCenter(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text);
+static FC_Rect FC_RenderRight(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text);
+
+
+static_inline SDL_Surface* FC_CreateSurface32(Uint32 width, Uint32 height)
+{
+    #if SDL_BYTEORDER == SDL_BIG_ENDIAN
+        return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
+    #else
+        return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
+    #endif
+}
+
+
+char* U8_alloc(unsigned int size)
+{
+    char* result;
+    if(size == 0)
+        return NULL;
+
+    result = (char*)malloc(size);
+    result[0] = '\0';
+
+    return result;
+}
+
+void U8_free(char* string)
+{
+    free(string);
+}
+
+char* U8_strdup(const char* string)
+{
+    char* result;
+    if(string == NULL)
+        return NULL;
+
+    result = (char*)malloc(strlen(string)+1);
+    strcpy(result, string);
+
+    return result;
+}
+
+int U8_strlen(const char* string)
+{
+    int length = 0;
+    if(string == NULL)
+        return 0;
+
+    while(*string != '\0')
+    {
+        string = U8_next(string);
+        ++length;
+    }
+
+    return length;
+}
+
+int U8_charsize(const char* character)
+{
+    if(character == NULL)
+        return 0;
+
+    if((unsigned char)*character <= 0x7F)
+        return 1;
+    else if((unsigned char)*character < 0xE0)
+        return 2;
+    else if((unsigned char)*character < 0xF0)
+        return 3;
+    else
+        return 4;
+    return 1;
+}
+
+int U8_charcpy(char* buffer, const char* source, int buffer_size)
+{
+    int charsize;
+    if(buffer == NULL || source == NULL || buffer_size < 1)
+        return 0;
+
+    charsize = U8_charsize(source);
+    if(charsize > buffer_size)
+        return 0;
+
+    memcpy(buffer, source, charsize);
+    return charsize;
+}
+
+const char* U8_next(const char* string)
+{
+    return string + U8_charsize(string);
+}
+
+int U8_strinsert(char* string, int position, const char* source, int max_bytes)
+{
+    int pos_u8char;
+    int len;
+    int add_len;
+    int ulen;
+    const char* string_start = string;
+
+    if(string == NULL || source == NULL)
+        return 0;
+
+    len = strlen(string);
+    add_len = strlen(source);
+    ulen = U8_strlen(string);
+
+    if(position == -1)
+        position = ulen;
+
+    if(position < 0 || position > ulen || len + add_len + 1 > max_bytes)
+        return 0;
+
+    // Move string pointer to the proper position
+    pos_u8char = 0;
+    while(*string != '\0' && pos_u8char < position)
+    {
+        string = (char*)U8_next(string);
+        ++pos_u8char;
+    }
+
+    // Move the rest of the string out of the way
+    memmove(string + add_len, string, len - (string - string_start) + 1);
+
+    // Copy in the new characters
+    memcpy(string, source, add_len);
+
+    return 1;
+}
+
+void U8_strdel(char* string, int position)
+{
+    if(string == NULL || position < 0)
+        return;
+
+    while(*string != '\0')
+    {
+        if(position == 0)
+        {
+            int chars_to_erase = U8_charsize(string);
+            int remaining_bytes = strlen(string) + 1;
+            memmove(string, string + chars_to_erase, remaining_bytes);
+            break;
+        }
+
+        string = (char*)U8_next(string);
+        --position;
+    }
+}
+
+
+
+
+
+static_inline FC_Rect FC_RectUnion(FC_Rect A, FC_Rect B)
+{
+    float x,x2,y,y2;
+    x = FC_MIN(A.x, B.x);
+    y = FC_MIN(A.y, B.y);
+    x2 = FC_MAX(A.x+A.w, B.x+B.w);
+    y2 = FC_MAX(A.y+A.h, B.y+B.h);
+    {
+        FC_Rect result = {x, y, FC_MAX(0, x2 - x), FC_MAX(0, y2 - y)};
+        return result;
+    }
+}
+
+// Adapted from SDL_IntersectRect
+static_inline FC_Rect FC_RectIntersect(FC_Rect A, FC_Rect B)
+{
+    FC_Rect result;
+	float Amin, Amax, Bmin, Bmax;
+
+	// Horizontal intersection
+	Amin = A.x;
+	Amax = Amin + A.w;
+	Bmin = B.x;
+	Bmax = Bmin + B.w;
+	if(Bmin > Amin)
+	        Amin = Bmin;
+	result.x = Amin;
+	if(Bmax < Amax)
+	        Amax = Bmax;
+	result.w = Amax - Amin > 0 ? Amax - Amin : 0;
+
+	// Vertical intersection
+	Amin = A.y;
+	Amax = Amin + A.h;
+	Bmin = B.y;
+	Bmax = Bmin + B.h;
+	if(Bmin > Amin)
+	        Amin = Bmin;
+	result.y = Amin;
+	if(Bmax < Amax)
+	        Amax = Bmax;
+	result.h = Amax - Amin > 0 ? Amax - Amin : 0;
+
+	return result;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+FC_Rect FC_DefaultRenderCallback(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale)
+{
+    float w = srcrect->w * xscale;
+    float h = srcrect->h * yscale;
+    FC_Rect result;
+
+    // FIXME: Why does the scaled offset look so wrong?
+    #ifdef FC_USE_SDL_GPU
+    {
+        GPU_Rect r = *srcrect;
+        GPU_BlitScale(src, &r, dest, x + xscale*r.w/2.0f, y + r.h/2.0f, xscale, yscale);
+    }
+    #else
+    {
+        SDL_RendererFlip flip = SDL_FLIP_NONE;
+        if(xscale < 0)
+        {
+            xscale = -xscale;
+            flip = (SDL_RendererFlip) ((int)flip | (int)SDL_FLIP_HORIZONTAL);
+        }
+        if(yscale < 0)
+        {
+            yscale = -yscale;
+            flip = (SDL_RendererFlip) ((int)flip | (int)SDL_FLIP_VERTICAL);
+        }
+
+        SDL_Rect r = *srcrect;
+        SDL_Rect dr = {(int)x, (int)y, (int)(xscale*r.w), (int)(yscale*r.h)};
+        SDL_RenderCopyEx(dest, src, &r, &dr, 0, NULL, flip);
+    }
+    #endif
+
+    result.x = x;
+    result.y = y;
+    result.w = w;
+    result.h = h;
+    return result;
+}
+
+static FC_Rect (*fc_render_callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale) = &FC_DefaultRenderCallback;
+
+void FC_SetRenderCallback(FC_Rect (*callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale))
+{
+    if(callback == NULL)
+        fc_render_callback = &FC_DefaultRenderCallback;
+    else
+        fc_render_callback = callback;
+}
+
+void FC_GetUTF8FromCodepoint(char* result, Uint32 codepoint)
+{
+    char a, b, c, d;
+
+    if(result == NULL)
+        return;
+
+    a = (codepoint >> 24) & 0xFF;
+    b = (codepoint >> 16) & 0xFF;
+    c = (codepoint >> 8) & 0xFF;
+    d = codepoint & 0xFF;
+
+    if(a == 0)
+    {
+        if(b == 0)
+        {
+            if(c == 0)
+            {
+                result[0] = d;
+                result[1] = '\0';
+            }
+            else
+            {
+                result[0] = c;
+                result[1] = d;
+                result[2] = '\0';
+            }
+        }
+        else
+        {
+            result[0] = b;
+            result[1] = c;
+            result[2] = d;
+            result[3] = '\0';
+        }
+    }
+    else
+    {
+        result[0] = a;
+        result[1] = b;
+        result[2] = c;
+        result[3] = d;
+        result[4] = '\0';
+    }
+}
+
+Uint32 FC_GetCodepointFromUTF8(const char** c, Uint8 advance_pointer)
+{
+    Uint32 result = 0;
+    const char* str;
+    if(c == NULL || *c == NULL)
+        return 0;
+
+    str = *c;
+    if((unsigned char)*str <= 0x7F)
+        result = *str;
+    else if((unsigned char)*str < 0xE0)
+    {
+        result |= (unsigned char)(*str) << 8;
+        result |= (unsigned char)(*(str+1));
+        if(advance_pointer)
+            *c += 1;
+    }
+    else if((unsigned char)*str < 0xF0)
+    {
+        result |= (unsigned char)(*str) << 16;
+        result |= (unsigned char)(*(str+1)) << 8;
+        result |= (unsigned char)(*(str+2));
+        if(advance_pointer)
+            *c += 2;
+    }
+    else
+    {
+        result |= (unsigned char)(*str) << 24;
+        result |= (unsigned char)(*(str+1)) << 16;
+        result |= (unsigned char)(*(str+2)) << 8;
+        result |= (unsigned char)(*(str+3));
+        if(advance_pointer)
+            *c += 3;
+    }
+    return result;
+}
+
+
+void FC_SetLoadingString(FC_Font* font, const char* string)
+{
+    if(font == NULL)
+        return;
+
+    free(font->loading_string);
+    font->loading_string = U8_strdup(string);
+}
+
+
+unsigned int FC_GetBufferSize(void)
+{
+    return fc_buffer_size;
+}
+
+void FC_SetBufferSize(unsigned int size)
+{
+    free(fc_buffer);
+    if(size > 0)
+    {
+        fc_buffer_size = size;
+        fc_buffer = (char*)malloc(fc_buffer_size);
+    }
+    else
+        fc_buffer = (char*)malloc(fc_buffer_size);
+}
+
+
+unsigned int FC_GetTabWidth(void)
+{
+    return fc_tab_width;
+}
+
+void FC_SetTabWidth(unsigned int width_in_spaces)
+{
+    fc_tab_width = width_in_spaces;
+}
+
+
+
+
+
+// Constructors
+
+static void FC_Init(FC_Font* font)
+{
+    if(font == NULL)
+        return;
+
+    #ifndef FC_USE_SDL_GPU
+    font->renderer = NULL;
+    #endif
+
+    font->ttf_source = NULL;
+    font->owns_ttf_source = 0;
+
+    font->filter = FC_FILTER_NEAREST;
+
+    font->default_color.r = 0;
+    font->default_color.g = 0;
+    font->default_color.b = 0;
+    FC_GET_ALPHA(font->default_color) = 255;
+
+    font->height = 0; // ascent+descent
+
+    font->maxWidth = 0;
+    font->baseline = 0;
+    font->ascent = 0;
+    font->descent = 0;
+
+    font->lineSpacing = 0;
+    font->letterSpacing = 0;
+
+    // Give a little offset for when filtering/mipmaps are used.  Depending on mipmap level, this will still not be enough.
+    font->last_glyph.rect.x = FC_CACHE_PADDING;
+    font->last_glyph.rect.y = FC_CACHE_PADDING;
+    font->last_glyph.rect.w = 0;
+    font->last_glyph.rect.h = 0;
+    font->last_glyph.cache_level = 0;
+
+    if(font->glyphs != NULL)
+        FC_MapFree(font->glyphs);
+
+    font->glyphs = FC_MapCreate(FC_DEFAULT_NUM_BUCKETS);
+
+    font->glyph_cache_size = 3;
+    font->glyph_cache_count = 0;
+
+
+    font->glyph_cache = (FC_Image**)malloc(font->glyph_cache_size * sizeof(FC_Image*));
+
+	if (font->loading_string == NULL)
+		font->loading_string = FC_GetStringASCII();
+
+    if(fc_buffer == NULL)
+        fc_buffer = (char*)malloc(fc_buffer_size);
+}
+
+static Uint8 FC_GrowGlyphCache(FC_Font* font)
+{
+    if(font == NULL)
+        return 0;
+    #ifdef FC_USE_SDL_GPU
+    GPU_Image* new_level = GPU_CreateImage(font->height * 12, font->height * 12, GPU_FORMAT_RGBA);
+    GPU_SetAnchor(new_level, 0.5f, 0.5f);  // Just in case the default is different
+    #else
+    SDL_Texture* new_level = SDL_CreateTexture(font->renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, font->height * 12, font->height * 12);
+    #endif
+    if(new_level == NULL || !FC_SetGlyphCacheLevel(font, font->glyph_cache_count, new_level))
+    {
+        FC_Log("Error: SDL_FontCache ran out of packing space and could not add another cache level.\n");
+        #ifdef FC_USE_SDL_GPU
+        GPU_FreeImage(new_level);
+        #else
+        SDL_DestroyTexture(new_level);
+        #endif
+        return 0;
+    }
+    // bug: we do not have the correct color here, this might be the wrong color!
+    //      , most functions use set_color_for_all_caches()
+    //   - for evading this bug, you must use FC_SetDefaultColor(), before using any draw functions
+    set_color(new_level, font->default_color.r, font->default_color.g, font->default_color.b, FC_GET_ALPHA(font->default_color));
+#ifndef FC_USE_SDL_GPU
+    {
+        Uint8 r, g, b, a;
+        SDL_Texture* prev_target = SDL_GetRenderTarget(font->renderer);
+        SDL_Rect prev_clip, prev_viewport;
+        int prev_logicalw, prev_logicalh;
+        Uint8 prev_clip_enabled;
+        float prev_scalex, prev_scaley;
+        // only backup if previous target existed (SDL will preserve them for the default target)
+        if (prev_target) {
+            prev_clip_enabled = has_clip(font->renderer);
+            if (prev_clip_enabled)
+                prev_clip = get_clip(font->renderer);
+            SDL_RenderGetViewport(font->renderer, &prev_viewport);
+            SDL_RenderGetScale(font->renderer, &prev_scalex, &prev_scaley);
+            SDL_RenderGetLogicalSize(font->renderer, &prev_logicalw, &prev_logicalh);
+        }
+        SDL_SetTextureBlendMode(new_level, SDL_BLENDMODE_BLEND);
+        SDL_SetRenderTarget(font->renderer, new_level);
+        SDL_GetRenderDrawColor(font->renderer, &r, &g, &b, &a);
+        SDL_SetRenderDrawColor(font->renderer, 0, 0, 0, 0);
+        SDL_RenderClear(font->renderer);
+        SDL_SetRenderDrawColor(font->renderer, r, g, b, a);
+        SDL_SetRenderTarget(font->renderer, prev_target);
+        if (prev_target) {
+            if (prev_clip_enabled)
+                set_clip(font->renderer, &prev_clip);
+            if (prev_logicalw && prev_logicalh)
+                SDL_RenderSetLogicalSize(font->renderer, prev_logicalw, prev_logicalh);
+            else {
+                SDL_RenderSetViewport(font->renderer, &prev_viewport);
+                SDL_RenderSetScale(font->renderer, prev_scalex, prev_scaley);
+            }
+        }
+    }
+#endif
+    return 1;
+}
+
+Uint8 FC_UploadGlyphCache(FC_Font* font, int cache_level, SDL_Surface* data_surface)
+{
+    if(font == NULL || data_surface == NULL)
+        return 0;
+    #ifdef FC_USE_SDL_GPU
+    GPU_Image* new_level = GPU_CopyImageFromSurface(data_surface);
+    GPU_SetAnchor(new_level, 0.5f, 0.5f);  // Just in case the default is different
+    if(FC_GetFilterMode(font) == FC_FILTER_LINEAR)
+        GPU_SetImageFilter(new_level, GPU_FILTER_LINEAR);
+    else
+        GPU_SetImageFilter(new_level, GPU_FILTER_NEAREST);
+    #else
+    SDL_Texture* new_level;
+    if(!fc_has_render_target_support)
+        new_level = SDL_CreateTextureFromSurface(font->renderer, data_surface);
+    else
+    {
+        // Must upload with render target enabled so we can put more glyphs on later
+        SDL_Renderer* renderer = font->renderer;
+
+        // Set filter mode for new texture
+        char old_filter_mode[16];  // Save it so we can change the hint value in the meantime
+        const char* old_filter_hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
+        if(!old_filter_hint)
+            old_filter_hint = "nearest";
+        snprintf(old_filter_mode, 16, "%s", old_filter_hint);
+
+        if(FC_GetFilterMode(font) == FC_FILTER_LINEAR)
+            SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
+        else
+            SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
+
+        new_level = SDL_CreateTexture(renderer, data_surface->format->format, SDL_TEXTUREACCESS_TARGET, data_surface->w, data_surface->h);
+        SDL_SetTextureBlendMode(new_level, SDL_BLENDMODE_BLEND);
+
+        // Reset filter mode for the temp texture
+        SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
+
+        {
+            Uint8 r, g, b, a;
+            SDL_Texture* temp = SDL_CreateTextureFromSurface(renderer, data_surface);
+            SDL_Texture* prev_target = SDL_GetRenderTarget(renderer);
+            SDL_Rect prev_clip, prev_viewport;
+            int prev_logicalw, prev_logicalh;
+            Uint8 prev_clip_enabled;
+            float prev_scalex, prev_scaley;
+            // only backup if previous target existed (SDL will preserve them for the default target)
+            if (prev_target) {
+                prev_clip_enabled = has_clip(renderer);
+                if (prev_clip_enabled)
+                    prev_clip = get_clip(renderer);
+                SDL_RenderGetViewport(renderer, &prev_viewport);
+                SDL_RenderGetScale(renderer, &prev_scalex, &prev_scaley);
+                SDL_RenderGetLogicalSize(renderer, &prev_logicalw, &prev_logicalh);
+            }
+            SDL_SetTextureBlendMode(temp, SDL_BLENDMODE_NONE);
+            SDL_SetRenderTarget(renderer, new_level);
+
+            SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a);
+            SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
+            SDL_RenderClear(renderer);
+            SDL_SetRenderDrawColor(renderer, r, g, b, a);
+
+            SDL_RenderCopy(renderer, temp, NULL, NULL);
+            SDL_SetRenderTarget(renderer, prev_target);
+            if (prev_target) {
+                if (prev_clip_enabled)
+                    set_clip(renderer, &prev_clip);
+                if (prev_logicalw && prev_logicalh)
+                    SDL_RenderSetLogicalSize(renderer, prev_logicalw, prev_logicalh);
+                else {
+                    SDL_RenderSetViewport(renderer, &prev_viewport);
+                    SDL_RenderSetScale(renderer, prev_scalex, prev_scaley);
+                }
+            }
+
+            SDL_DestroyTexture(temp);
+        }
+
+        // Reset to the old filter value
+        SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, old_filter_mode);
+
+    }
+    #endif
+    if(new_level == NULL || !FC_SetGlyphCacheLevel(font, cache_level, new_level))
+    {
+        FC_Log("Error: SDL_FontCache ran out of packing space and could not add another cache level.\n");
+        #ifdef FC_USE_SDL_GPU
+        GPU_FreeImage(new_level);
+        #else
+        SDL_DestroyTexture(new_level);
+        #endif
+        return 0;
+    }
+    return 1;
+}
+
+static FC_GlyphData* FC_PackGlyphData(FC_Font* font, Uint32 codepoint, Uint16 width, Uint16 maxWidth, Uint16 maxHeight)
+{
+    FC_Map* glyphs = font->glyphs;
+    FC_GlyphData* last_glyph = &font->last_glyph;
+    Uint16 height = font->height + FC_CACHE_PADDING;
+
+    // TAB is special!
+    if(codepoint == '\t')
+    {
+        FC_GlyphData spaceGlyph;
+        FC_GetGlyphData(font, &spaceGlyph, ' ');
+        width = fc_tab_width * spaceGlyph.rect.w;
+    }
+
+    if(last_glyph->rect.x + last_glyph->rect.w + width >= maxWidth - FC_CACHE_PADDING)
+    {
+        if(last_glyph->rect.y + height + height >= maxHeight - FC_CACHE_PADDING)
+        {
+            // Get ready to pack on the next cache level when it is ready
+            last_glyph->cache_level = font->glyph_cache_count;
+            last_glyph->rect.x = FC_CACHE_PADDING;
+            last_glyph->rect.y = FC_CACHE_PADDING;
+            last_glyph->rect.w = 0;
+            return NULL;
+        }
+        else
+        {
+            // Go to next row
+            last_glyph->rect.x = FC_CACHE_PADDING;
+            last_glyph->rect.y += height;
+            last_glyph->rect.w = 0;
+        }
+    }
+
+    // Move to next space
+    last_glyph->rect.x += last_glyph->rect.w + 1 + FC_CACHE_PADDING;
+    last_glyph->rect.w = width;
+
+    return FC_MapInsert(glyphs, codepoint, FC_MakeGlyphData(last_glyph->cache_level, last_glyph->rect.x, last_glyph->rect.y, last_glyph->rect.w, last_glyph->rect.h));
+}
+
+
+FC_Image* FC_GetGlyphCacheLevel(FC_Font* font, int cache_level)
+{
+    if(font == NULL || cache_level < 0 || cache_level > font->glyph_cache_count)
+        return NULL;
+
+    return font->glyph_cache[cache_level];
+}
+
+Uint8 FC_SetGlyphCacheLevel(FC_Font* font, int cache_level, FC_Image* cache_texture)
+{
+    if(font == NULL || cache_level < 0)
+        return 0;
+
+    // Must be sequentially added
+    if(cache_level > font->glyph_cache_count + 1)
+        return 0;
+
+    if(cache_level == font->glyph_cache_count)
+    {
+        font->glyph_cache_count++;
+
+        // Grow cache?
+        if(font->glyph_cache_count > font->glyph_cache_size)
+        {
+            // Copy old cache to new one
+            int i;
+            FC_Image** new_cache;
+            new_cache = (FC_Image**)malloc(font->glyph_cache_count * sizeof(FC_Image*));
+            for(i = 0; i < font->glyph_cache_size; ++i)
+                new_cache[i] = font->glyph_cache[i];
+
+            // Save new cache
+            free(font->glyph_cache);
+            font->glyph_cache_size = font->glyph_cache_count;
+            font->glyph_cache = new_cache;
+        }
+    }
+
+    font->glyph_cache[cache_level] = cache_texture;
+    return 1;
+}
+
+
+FC_Font* FC_CreateFont(void)
+{
+    FC_Font* font;
+
+    font = (FC_Font*)malloc(sizeof(FC_Font));
+    memset(font, 0, sizeof(FC_Font));
+
+    FC_Init(font);
+    ++NUM_EXISTING_FONTS;
+
+    return font;
+}
+
+
+// Assume this many will be enough...
+#define FC_LOAD_MAX_SURFACES 10
+
+#ifdef FC_USE_SDL_GPU
+Uint8 FC_LoadFontFromTTF(FC_Font* font, TTF_Font* ttf, SDL_Color color)
+#else
+Uint8 FC_LoadFontFromTTF(FC_Font* font, SDL_Renderer* renderer, TTF_Font* ttf, SDL_Color color)
+#endif
+{
+    if(font == NULL || ttf == NULL)
+        return 0;
+    #ifndef FC_USE_SDL_GPU
+    if(renderer == NULL)
+        return 0;
+    #endif
+
+    FC_ClearFont(font);
+
+
+    // Might as well check render target support here
+    #ifdef FC_USE_SDL_GPU
+    fc_has_render_target_support = GPU_IsFeatureEnabled(GPU_FEATURE_RENDER_TARGETS);
+    #else
+    SDL_RendererInfo info;
+    SDL_GetRendererInfo(renderer, &info);
+    fc_has_render_target_support = (info.flags & SDL_RENDERER_TARGETTEXTURE);
+
+    font->renderer = renderer;
+    #endif
+
+    font->ttf_source = ttf;
+
+    //font->line_height = TTF_FontLineSkip(ttf);
+    font->height = TTF_FontHeight(ttf);
+    font->ascent = TTF_FontAscent(ttf);
+    font->descent = -TTF_FontDescent(ttf);
+
+    // Some bug for certain fonts can result in an incorrect height.
+    if(font->height < font->ascent - font->descent)
+        font->height = font->ascent - font->descent;
+
+    font->baseline = font->height - font->descent;
+
+    font->default_color = color;
+
+    {
+        SDL_Color white = {255, 255, 255, 255};
+        SDL_Surface* glyph_surf;
+        char buff[5];
+        const char* buff_ptr = buff;
+        const char* source_string;
+        Uint8 packed = 0;
+
+        // Copy glyphs from the surface to the font texture and store the position data
+        // Pack row by row into a square texture
+        // Try figuring out dimensions that make sense for the font size.
+        unsigned int w = font->height*12;
+        unsigned int h = font->height*12;
+        SDL_Surface* surfaces[FC_LOAD_MAX_SURFACES];
+        int num_surfaces = 1;
+        surfaces[0] = FC_CreateSurface32(w, h);
+        font->last_glyph.rect.x = FC_CACHE_PADDING;
+        font->last_glyph.rect.y = FC_CACHE_PADDING;
+        font->last_glyph.rect.w = 0;
+        font->last_glyph.rect.h = font->height;
+
+        source_string = font->loading_string;
+        for(; *source_string != '\0'; source_string = U8_next(source_string))
+        {
+            memset(buff, 0, 5);
+            if(!U8_charcpy(buff, source_string, 5))
+                continue;
+            glyph_surf = TTF_RenderUTF8_Blended(ttf, buff, white);
+            if(glyph_surf == NULL)
+                continue;
+
+            // Try packing.  If it fails, create a new surface for the next cache level.
+            packed = (FC_PackGlyphData(font, FC_GetCodepointFromUTF8(&buff_ptr, 0), glyph_surf->w, surfaces[num_surfaces-1]->w, surfaces[num_surfaces-1]->h) != NULL);
+            if(!packed)
+            {
+                int i = num_surfaces-1;
+                if(num_surfaces >= FC_LOAD_MAX_SURFACES)
+                {
+                    // Can't do any more!
+                    FC_Log("SDL_FontCache error: Could not create enough cache surfaces to fit all of the loading string!\n");
+                    SDL_FreeSurface(glyph_surf);
+                    break;
+                }
+
+                // Upload the current surface to the glyph cache now so we can keep the cache level packing cursor up to date as we go.
+                FC_UploadGlyphCache(font, i, surfaces[i]);
+                SDL_FreeSurface(surfaces[i]);
+                #ifndef FC_USE_SDL_GPU
+                SDL_SetTextureBlendMode(font->glyph_cache[i], SDL_BLENDMODE_BLEND);
+                #endif
+                // Update the glyph cursor to the new cache level.  We need to do this here because the actual cache lags behind our use of the packing above.
+                font->last_glyph.cache_level = num_surfaces;
+
+
+                surfaces[num_surfaces] = FC_CreateSurface32(w, h);
+                num_surfaces++;
+            }
+
+            // Try packing for the new surface, then blit onto it.
+            if(packed || FC_PackGlyphData(font, FC_GetCodepointFromUTF8(&buff_ptr, 0), glyph_surf->w, surfaces[num_surfaces-1]->w, surfaces[num_surfaces-1]->h) != NULL)
+            {
+                SDL_SetSurfaceBlendMode(glyph_surf, SDL_BLENDMODE_NONE);
+                SDL_Rect srcRect = {0, 0, glyph_surf->w, glyph_surf->h};
+                SDL_Rect destrect = font->last_glyph.rect;
+                SDL_BlitSurface(glyph_surf, &srcRect, surfaces[num_surfaces-1], &destrect);
+            }
+
+            SDL_FreeSurface(glyph_surf);
+        }
+
+        {
+            int i = num_surfaces-1;
+            FC_UploadGlyphCache(font, i, surfaces[i]);
+            SDL_FreeSurface(surfaces[i]);
+            #ifndef FC_USE_SDL_GPU
+            SDL_SetTextureBlendMode(font->glyph_cache[i], SDL_BLENDMODE_BLEND);
+            #endif
+        }
+    }
+
+    return 1;
+}
+
+
+#ifdef FC_USE_SDL_GPU
+Uint8 FC_LoadFont(FC_Font* font, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style)
+#else
+Uint8 FC_LoadFont(FC_Font* font, FC_Target* renderer, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style)
+#endif
+{
+    SDL_RWops* rwops;
+
+    if(font == NULL)
+        return 0;
+
+    rwops = SDL_RWFromFile(filename_ttf, "rb");
+
+    if(rwops == NULL)
+    {
+        FC_Log("Unable to open file for reading: %s \n", SDL_GetError());
+        return 0;
+    }
+
+    #ifdef FC_USE_SDL_GPU
+    return FC_LoadFont_RW(font, rwops, 1, pointSize, color, style);
+    #else
+    return FC_LoadFont_RW(font, renderer, rwops, 1, pointSize, color, style);
+    #endif
+}
+
+#ifdef FC_USE_SDL_GPU
+Uint8 FC_LoadFont_RW(FC_Font* font, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style)
+#else
+Uint8 FC_LoadFont_RW(FC_Font* font, FC_Target* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style)
+#endif
+{
+    Uint8 result;
+    TTF_Font* ttf;
+    Uint8 outline;
+
+    if(font == NULL)
+        return 0;
+
+    if(!TTF_WasInit() && TTF_Init() < 0)
+    {
+        FC_Log("Unable to initialize SDL_ttf: %s \n", TTF_GetError());
+        if(own_rwops)
+            SDL_RWclose(file_rwops_ttf);
+        return 0;
+    }
+
+    ttf = TTF_OpenFontRW(file_rwops_ttf, own_rwops, pointSize);
+
+    if(ttf == NULL)
+    {
+        FC_Log("Unable to load TrueType font: %s \n", TTF_GetError());
+        if(own_rwops)
+            SDL_RWclose(file_rwops_ttf);
+        return 0;
+    }
+
+    outline = (style & TTF_STYLE_OUTLINE);
+    if(outline)
+    {
+        style &= ~TTF_STYLE_OUTLINE;
+        TTF_SetFontOutline(ttf, 1);
+    }
+    TTF_SetFontStyle(ttf, style);
+
+    #ifdef FC_USE_SDL_GPU
+    result = FC_LoadFontFromTTF(font, ttf, color);
+    #else
+    result = FC_LoadFontFromTTF(font, renderer, ttf, color);
+    #endif
+
+    // Can only load new (uncached) glyphs if we can keep the SDL_RWops open.
+    font->owns_ttf_source = own_rwops;
+    if(!own_rwops)
+    {
+        TTF_CloseFont(font->ttf_source);
+        font->ttf_source = NULL;
+    }
+
+    return result;
+}
+
+
+#ifndef FC_USE_SDL_GPU
+void FC_ResetFontFromRendererReset(FC_Font* font, SDL_Renderer* renderer, Uint32 evType)
+{
+    TTF_Font* ttf;
+    SDL_Color col;
+    Uint8 owns_ttf;
+    if (font == NULL)
+        return;
+
+    // Destroy glyph cache
+    if (evType == SDL_RENDER_TARGETS_RESET) {
+        int i;
+        for (i = 0; i < font->glyph_cache_count; ++i)
+            SDL_DestroyTexture(font->glyph_cache[i]);
+    }
+    free(font->glyph_cache);
+
+    ttf = font->ttf_source;
+    col = font->default_color;
+    owns_ttf = font->owns_ttf_source;
+    FC_Init(font);
+
+    // Can only reload glyphs if we own the SDL_RWops.
+    if (owns_ttf)
+        FC_LoadFontFromTTF(font, renderer, ttf, col);
+    font->owns_ttf_source = owns_ttf;
+}
+#endif
+
+void FC_ClearFont(FC_Font* font)
+{
+    int i;
+    if(font == NULL)
+        return;
+
+    // Release resources
+    if(font->owns_ttf_source)
+        TTF_CloseFont(font->ttf_source);
+
+    font->owns_ttf_source = 0;
+    font->ttf_source = NULL;
+
+    // Delete glyph map
+    FC_MapFree(font->glyphs);
+    font->glyphs = NULL;
+
+    // Delete glyph cache
+    for(i = 0; i < font->glyph_cache_count; ++i)
+    {
+        #ifdef FC_USE_SDL_GPU
+        GPU_FreeImage(font->glyph_cache[i]);
+        #else
+        SDL_DestroyTexture(font->glyph_cache[i]);
+        #endif
+    }
+    free(font->glyph_cache);
+    font->glyph_cache = NULL;
+
+    // Reset font
+    FC_Init(font);
+}
+
+
+void FC_FreeFont(FC_Font* font)
+{
+    int i;
+    if(font == NULL)
+        return;
+
+    // Release resources
+    if(font->owns_ttf_source)
+        TTF_CloseFont(font->ttf_source);
+
+    // Delete glyph map
+    FC_MapFree(font->glyphs);
+
+    // Delete glyph cache
+    for(i = 0; i < font->glyph_cache_count; ++i)
+    {
+        #ifdef FC_USE_SDL_GPU
+        GPU_FreeImage(font->glyph_cache[i]);
+        #else
+        SDL_DestroyTexture(font->glyph_cache[i]);
+        #endif
+    }
+    free(font->glyph_cache);
+
+    free(font->loading_string);
+
+    free(font);
+
+    // If the last font has been freed; assume shutdown and free the global variables
+    if (--NUM_EXISTING_FONTS <= 0)
+    {
+        free(ASCII_STRING);
+        ASCII_STRING = NULL;
+
+        free(LATIN_1_STRING);
+        LATIN_1_STRING = NULL;
+
+        free(ASCII_LATIN_1_STRING);
+        ASCII_LATIN_1_STRING = NULL;
+
+        free(fc_buffer);
+        fc_buffer = NULL;
+    }
+}
+
+int FC_GetNumCacheLevels(FC_Font* font)
+{
+    return font->glyph_cache_count;
+}
+
+Uint8 FC_AddGlyphToCache(FC_Font* font, SDL_Surface* glyph_surface)
+{
+    if(font == NULL || glyph_surface == NULL)
+        return 0;
+
+    SDL_SetSurfaceBlendMode(glyph_surface, SDL_BLENDMODE_NONE);
+    FC_Image* dest = FC_GetGlyphCacheLevel(font, font->last_glyph.cache_level);
+    if(dest == NULL)
+        return 0;
+
+    #ifdef FC_USE_SDL_GPU
+    {
+        GPU_Target* target = GPU_LoadTarget(dest);
+        if(target == NULL)
+            return 0;
+        GPU_Image* img = GPU_CopyImageFromSurface(glyph_surface);
+        GPU_SetAnchor(img, 0.5f, 0.5f);  // Just in case the default is different
+        GPU_SetImageFilter(img, GPU_FILTER_NEAREST);
+        GPU_SetBlendMode(img, GPU_BLEND_SET);
+
+        SDL_Rect destrect = font->last_glyph.rect;
+        GPU_Blit(img, NULL, target, destrect.x + destrect.w/2, destrect.y + destrect.h/2);
+
+        GPU_FreeImage(img);
+        GPU_FreeTarget(target);
+    }
+    #else
+    {
+        SDL_Renderer* renderer = font->renderer;
+        SDL_Texture* img;
+        SDL_Rect destrect;
+        SDL_Texture* prev_target = SDL_GetRenderTarget(renderer);
+        SDL_Rect prev_clip, prev_viewport;
+        int prev_logicalw, prev_logicalh;
+        Uint8 prev_clip_enabled;
+        float prev_scalex, prev_scaley;
+        // only backup if previous target existed (SDL will preserve them for the default target)
+        if (prev_target) {
+            prev_clip_enabled = has_clip(renderer);
+            if (prev_clip_enabled)
+                prev_clip = get_clip(renderer);
+            SDL_RenderGetViewport(renderer, &prev_viewport);
+            SDL_RenderGetScale(renderer, &prev_scalex, &prev_scaley);
+            SDL_RenderGetLogicalSize(renderer, &prev_logicalw, &prev_logicalh);
+        }
+
+        img = SDL_CreateTextureFromSurface(renderer, glyph_surface);
+
+        destrect = font->last_glyph.rect;
+        SDL_SetRenderTarget(renderer, dest);
+        SDL_RenderCopy(renderer, img, NULL, &destrect);
+        SDL_SetRenderTarget(renderer, prev_target);
+        if (prev_target) {
+            if (prev_clip_enabled)
+                set_clip(renderer, &prev_clip);
+            if (prev_logicalw && prev_logicalh)
+                SDL_RenderSetLogicalSize(renderer, prev_logicalw, prev_logicalh);
+            else {
+                SDL_RenderSetViewport(renderer, &prev_viewport);
+                SDL_RenderSetScale(renderer, prev_scalex, prev_scaley);
+            }
+        }
+
+        SDL_DestroyTexture(img);
+    }
+    #endif
+
+    return 1;
+}
+
+
+unsigned int FC_GetNumCodepoints(FC_Font* font)
+{
+    FC_Map* glyphs;
+    int i;
+    unsigned int result = 0;
+    if(font == NULL || font->glyphs == NULL)
+        return 0;
+
+    glyphs = font->glyphs;
+
+    for(i = 0; i < glyphs->num_buckets; ++i)
+    {
+        FC_MapNode* node;
+        for(node = glyphs->buckets[i]; node != NULL; node = node->next)
+        {
+            result++;
+        }
+    }
+
+    return result;
+}
+
+void FC_GetCodepoints(FC_Font* font, Uint32* result)
+{
+    FC_Map* glyphs;
+    int i;
+    unsigned int count = 0;
+    if(font == NULL || font->glyphs == NULL)
+        return;
+
+    glyphs = font->glyphs;
+
+    for(i = 0; i < glyphs->num_buckets; ++i)
+    {
+        FC_MapNode* node;
+        for(node = glyphs->buckets[i]; node != NULL; node = node->next)
+        {
+            result[count] = node->key;
+            count++;
+        }
+    }
+}
+
+Uint8 FC_GetGlyphData(FC_Font* font, FC_GlyphData* result, Uint32 codepoint)
+{
+    FC_GlyphData* e = FC_MapFind(font->glyphs, codepoint);
+    if(e == NULL)
+    {
+        char buff[5];
+        int w, h;
+        SDL_Color white = {255, 255, 255, 255};
+        SDL_Surface* surf;
+        FC_Image* cache_image;
+
+        if(font->ttf_source == NULL)
+            return 0;
+
+        FC_GetUTF8FromCodepoint(buff, codepoint);
+
+        cache_image = FC_GetGlyphCacheLevel(font, font->last_glyph.cache_level);
+        if(cache_image == NULL)
+        {
+            FC_Log("SDL_FontCache: Failed to load cache image, so cannot add new glyphs!\n");
+            return 0;
+        }
+
+        #ifdef FC_USE_SDL_GPU
+        w = cache_image->w;
+        h = cache_image->h;
+        #else
+        SDL_QueryTexture(cache_image, NULL, NULL, &w, &h);
+        #endif
+
+        surf = TTF_RenderUTF8_Blended(font->ttf_source, buff, white);
+        if(surf == NULL)
+        {
+            return 0;
+        }
+
+        e = FC_PackGlyphData(font, codepoint, surf->w, w, h);
+        if(e == NULL)
+        {
+            // Grow the cache
+            FC_GrowGlyphCache(font);
+
+            // Try packing again
+            e = FC_PackGlyphData(font, codepoint, surf->w, w, h);
+            if(e == NULL)
+            {
+                SDL_FreeSurface(surf);
+                return 0;
+            }
+        }
+
+        // Render onto the cache texture
+        FC_AddGlyphToCache(font, surf);
+
+        SDL_FreeSurface(surf);
+    }
+
+    if(result != NULL && e != NULL)
+        *result = *e;
+
+    return 1;
+}
+
+
+FC_GlyphData* FC_SetGlyphData(FC_Font* font, Uint32 codepoint, FC_GlyphData glyph_data)
+{
+    return FC_MapInsert(font->glyphs, codepoint, glyph_data);
+}
+
+
+
+// Drawing
+static FC_Rect FC_RenderLeft(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text)
+{
+    const char* c = text;
+    FC_Rect srcRect;
+    FC_Rect dstRect;
+    FC_Rect dirtyRect = FC_MakeRect(x, y, 0, 0);
+
+    FC_GlyphData glyph;
+    Uint32 codepoint;
+
+    float destX = x;
+    float destY = y;
+    float destH;
+    float destLineSpacing;
+    float destLetterSpacing;
+
+    if(font == NULL)
+        return dirtyRect;
+
+    destH = font->height * scale.y;
+    destLineSpacing = font->lineSpacing*scale.y;
+    destLetterSpacing = font->letterSpacing*scale.x;
+
+    if(c == NULL || font->glyph_cache_count == 0 || dest == NULL)
+        return dirtyRect;
+
+    int newlineX = x;
+
+    for(; *c != '\0'; c++)
+    {
+        if(*c == '\n')
+        {
+            destX = newlineX;
+            destY += destH + destLineSpacing;
+            continue;
+        }
+
+        codepoint = FC_GetCodepointFromUTF8(&c, 1);  // Increments 'c' to skip the extra UTF-8 bytes
+        if(!FC_GetGlyphData(font, &glyph, codepoint))
+        {
+            codepoint = ' ';
+            if(!FC_GetGlyphData(font, &glyph, codepoint))
+                continue;  // Skip bad characters
+        }
+
+        if (codepoint == ' ')
+        {
+            destX += glyph.rect.w*scale.x + destLetterSpacing;
+            continue;
+        }
+        /*if(destX >= dest->w)
+            continue;
+        if(destY >= dest->h)
+            continue;*/
+
+        #ifdef FC_USE_SDL_GPU
+        srcRect.x = glyph.rect.x;
+        srcRect.y = glyph.rect.y;
+        srcRect.w = glyph.rect.w;
+        srcRect.h = glyph.rect.h;
+        #else
+        srcRect = glyph.rect;
+        #endif
+        dstRect = fc_render_callback(FC_GetGlyphCacheLevel(font, glyph.cache_level), &srcRect, dest, destX, destY, scale.x, scale.y);
+        if(dirtyRect.w == 0 || dirtyRect.h == 0)
+            dirtyRect = dstRect;
+        else
+            dirtyRect = FC_RectUnion(dirtyRect, dstRect);
+
+        destX += glyph.rect.w*scale.x + destLetterSpacing;
+    }
+
+    return dirtyRect;
+}
+
+static void set_color_for_all_caches(FC_Font* font, SDL_Color color)
+{
+    // TODO: How can I predict which glyph caches are to be used?
+    FC_Image* img;
+    int i;
+    int num_levels = FC_GetNumCacheLevels(font);
+    for(i = 0; i < num_levels; ++i)
+    {
+        img = FC_GetGlyphCacheLevel(font, i);
+        set_color(img, color.r, color.g, color.b, FC_GET_ALPHA(color));
+    }
+}
+
+FC_Rect FC_Draw(FC_Font* font, FC_Target* dest, float x, float y, const char* formatted_text, ...)
+{
+    if(formatted_text == NULL || font == NULL)
+        return FC_MakeRect(x, y, 0, 0);
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    set_color_for_all_caches(font, font->default_color);
+
+    return FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer);
+}
+
+
+
+typedef struct FC_StringList
+{
+    char* value;
+    struct FC_StringList* next;
+} FC_StringList;
+
+void FC_StringListFree(FC_StringList* node)
+{
+    // Delete the nodes in order
+    while(node != NULL)
+    {
+        FC_StringList* last = node;
+        node = node->next;
+
+        free(last->value);
+        free(last);
+    }
+}
+
+FC_StringList** FC_StringListPushBack(FC_StringList** node, char* value, Uint8 copy)
+{
+    if(node == NULL)
+    {
+        return NULL;
+    }
+
+    // Get to the last node
+    while(*node != NULL)
+    {
+        node = &(*node)->next;
+    }
+
+    *node = (FC_StringList*)malloc(sizeof(FC_StringList));
+
+    (*node)->value = (copy? U8_strdup(value) : value);
+    (*node)->next = NULL;
+
+    return node;
+}
+
+FC_StringList** FC_StringListPushBackBytes(FC_StringList** node, const char* data, int num_bytes)
+{
+    if(node == NULL)
+    {
+        return node;
+    }
+
+    // Get to the last node
+    while(*node != NULL)
+    {
+        node = &(*node)->next;
+    }
+
+    *node = (FC_StringList*)malloc(sizeof(FC_StringList));
+
+    (*node)->value = (char*)malloc(num_bytes + 1);
+    memcpy((*node)->value, data, num_bytes);
+    (*node)->value[num_bytes] = '\0';
+    (*node)->next = NULL;
+
+    return node;
+}
+
+static FC_StringList* FC_Explode(const char* text, char delimiter)
+{
+    FC_StringList* head;
+    FC_StringList* new_node;
+    FC_StringList** node;
+    const char* start;
+    const char* end;
+    unsigned int size;
+    if(text == NULL)
+        return NULL;
+
+    head = NULL;
+    node = &head;
+
+    // Doesn't technically support UTF-8, but it's probably fine, right?
+    size = 0;
+    start = end = text;
+    while(1)
+    {
+        if(*end == delimiter || *end == '\0')
+        {
+            *node = (FC_StringList*)malloc(sizeof(FC_StringList));
+            new_node = *node;
+
+            new_node->value = (char*)malloc(size + 1);
+            memcpy(new_node->value, start, size);
+            new_node->value[size] = '\0';
+
+            new_node->next = NULL;
+
+            if(*end == '\0')
+                break;
+
+            node = &((*node)->next);
+            start = end+1;
+            size = 0;
+        }
+        else
+            ++size;
+
+        ++end;
+    }
+
+    return head;
+}
+
+static FC_StringList* FC_ExplodeBreakingSpace(const char* text, FC_StringList** spaces)
+{
+    FC_StringList* head;
+    FC_StringList** node;
+    const char* start;
+    const char* end;
+    unsigned int size;
+    if(text == NULL)
+        return NULL;
+
+    head = NULL;
+    node = &head;
+
+    // Warning: spaces must not be initialized before this function
+    *spaces = NULL;
+
+    // Doesn't technically support UTF-8, but it's probably fine, right?
+    size = 0;
+    start = end = text;
+    while(1)
+    {
+        // Add any characters here that should make separate words (except for \n?)
+        if(*end == ' ' || *end == '\t' || *end == '\0')
+        {
+            FC_StringListPushBackBytes(node, start, size);
+            FC_StringListPushBackBytes(spaces, end, 1);
+
+            if(*end == '\0')
+                break;
+
+            node = &((*node)->next);
+            start = end+1;
+            size = 0;
+        }
+        else
+            ++size;
+
+        ++end;
+    }
+
+    return head;
+}
+
+static FC_StringList* FC_ExplodeAndKeep(const char* text, char delimiter)
+{
+    FC_StringList* head;
+    FC_StringList** node;
+    const char* start;
+    const char* end;
+    unsigned int size;
+    if(text == NULL)
+        return NULL;
+
+    head = NULL;
+    node = &head;
+
+    // Doesn't technically support UTF-8, but it's probably fine, right?
+    size = 0;
+    start = end = text;
+    while(1)
+    {
+        if(*end == delimiter || *end == '\0')
+        {
+            FC_StringListPushBackBytes(node, start, size);
+
+            if(*end == '\0')
+                break;
+
+            node = &((*node)->next);
+            start = end;
+            size = 1;
+        }
+        else
+            ++size;
+
+        ++end;
+    }
+
+    return head;
+}
+
+static void FC_RenderAlign(FC_Font* font, FC_Target* dest, float x, float y, int width, FC_Scale scale, FC_AlignEnum align, const char* text)
+{
+    switch(align)
+    {
+        case FC_ALIGN_LEFT:
+            FC_RenderLeft(font, dest, x, y, scale, text);
+            break;
+        case FC_ALIGN_CENTER:
+            FC_RenderCenter(font, dest, x + width/2, y, scale, text);
+            break;
+        case FC_ALIGN_RIGHT:
+            FC_RenderRight(font, dest, x + width, y, scale, text);
+            break;
+    }
+}
+
+static FC_StringList* FC_GetBufferFitToColumn(FC_Font* font, int width, FC_Scale scale, Uint8 keep_newlines)
+{
+    FC_StringList* result = NULL;
+    FC_StringList** current = &result;
+
+    FC_StringList *ls, *iter;
+
+    ls = (keep_newlines? FC_ExplodeAndKeep(fc_buffer, '\n') : FC_Explode(fc_buffer, '\n'));
+    for(iter = ls; iter != NULL; iter = iter->next)
+    {
+        char* line = iter->value;
+
+        // If line is too long, then add words one at a time until we go over.
+        if(width > 0 && FC_GetWidth(font, "%s", line) > width)
+        {
+            FC_StringList *words, *word_iter, *spaces, *spaces_iter;
+
+            words = FC_ExplodeBreakingSpace(line, &spaces);
+            // Skip the first word for the iterator, so there will always be at least one word per line
+            line = new_concat(words->value, spaces->value);
+            for(word_iter = words->next, spaces_iter = spaces->next; word_iter != NULL && spaces_iter != NULL; word_iter = word_iter->next, spaces_iter = spaces_iter->next)
+            {
+                char* line_plus_word = new_concat(line, word_iter->value);
+                char* word_plus_space = new_concat(word_iter->value, spaces_iter->value);
+                if(FC_GetWidth(font, "%s", line_plus_word) > width)
+                {
+                    current = FC_StringListPushBack(current, line, 0);
+
+                    line = word_plus_space;
+                }
+                else
+                {
+                    replace_concat(&line, word_plus_space);
+                    free(word_plus_space);
+                }
+                free(line_plus_word);
+            }
+            current = FC_StringListPushBack(current, line, 0);
+            FC_StringListFree(words);
+            FC_StringListFree(spaces);
+        }
+        else
+        {
+            current = FC_StringListPushBack(current, line, 0);
+            iter->value = NULL;
+        }
+    }
+    FC_StringListFree(ls);
+
+    return result;
+}
+
+static void FC_DrawColumnFromBuffer(FC_Font* font, FC_Target* dest, FC_Rect box, int* total_height, FC_Scale scale, FC_AlignEnum align)
+{
+    int y = box.y;
+    FC_StringList *ls, *iter;
+
+    ls = FC_GetBufferFitToColumn(font, box.w, scale, 0);
+    for(iter = ls; iter != NULL; iter = iter->next)
+    {
+        FC_RenderAlign(font, dest, box.x, y, box.w, scale, align, iter->value);
+        y += FC_GetLineHeight(font);
+    }
+    FC_StringListFree(ls);
+
+    if(total_height != NULL)
+        *total_height = y - box.y;
+}
+
+FC_Rect FC_DrawBox(FC_Font* font, FC_Target* dest, FC_Rect box, const char* formatted_text, ...)
+{
+    Uint8 useClip;
+    if(formatted_text == NULL || font == NULL)
+        return FC_MakeRect(box.x, box.y, 0, 0);
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    useClip = has_clip(dest);
+    FC_Rect oldclip, newclip;
+    if(useClip)
+    {
+        oldclip = get_clip(dest);
+        newclip = FC_RectIntersect(oldclip, box);
+    }
+    else
+        newclip = box;
+
+    set_clip(dest, &newclip);
+
+    set_color_for_all_caches(font, font->default_color);
+
+    FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), FC_ALIGN_LEFT);
+
+    if(useClip)
+        set_clip(dest, &oldclip);
+    else
+        set_clip(dest, NULL);
+
+    return box;
+}
+
+FC_Rect FC_DrawBoxAlign(FC_Font* font, FC_Target* dest, FC_Rect box, FC_AlignEnum align, const char* formatted_text, ...)
+{
+    Uint8 useClip;
+    if(formatted_text == NULL || font == NULL)
+        return FC_MakeRect(box.x, box.y, 0, 0);
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    useClip = has_clip(dest);
+    FC_Rect oldclip, newclip;
+    if(useClip)
+    {
+        oldclip = get_clip(dest);
+        newclip = FC_RectIntersect(oldclip, box);
+    }
+    else
+        newclip = box;
+    set_clip(dest, &newclip);
+
+    set_color_for_all_caches(font, font->default_color);
+
+    FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), align);
+
+    if(useClip)
+        set_clip(dest, &oldclip);
+    else
+        set_clip(dest, NULL);
+
+    return box;
+}
+
+FC_Rect FC_DrawBoxScale(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Scale scale, const char* formatted_text, ...)
+{
+    Uint8 useClip;
+    if(formatted_text == NULL || font == NULL)
+        return FC_MakeRect(box.x, box.y, 0, 0);
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    useClip = has_clip(dest);
+    FC_Rect oldclip, newclip;
+    if(useClip)
+    {
+        oldclip = get_clip(dest);
+        newclip = FC_RectIntersect(oldclip, box);
+    }
+    else
+        newclip = box;
+    set_clip(dest, &newclip);
+
+    set_color_for_all_caches(font, font->default_color);
+
+    FC_DrawColumnFromBuffer(font, dest, box, NULL, scale, FC_ALIGN_LEFT);
+
+    if(useClip)
+        set_clip(dest, &oldclip);
+    else
+        set_clip(dest, NULL);
+
+    return box;
+}
+
+FC_Rect FC_DrawBoxColor(FC_Font* font, FC_Target* dest, FC_Rect box, SDL_Color color, const char* formatted_text, ...)
+{
+    Uint8 useClip;
+    if(formatted_text == NULL || font == NULL)
+        return FC_MakeRect(box.x, box.y, 0, 0);
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    useClip = has_clip(dest);
+    FC_Rect oldclip, newclip;
+    if(useClip)
+    {
+        oldclip = get_clip(dest);
+        newclip = FC_RectIntersect(oldclip, box);
+    }
+    else
+        newclip = box;
+    set_clip(dest, &newclip);
+
+    set_color_for_all_caches(font, color);
+
+    FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), FC_ALIGN_LEFT);
+
+    if(useClip)
+        set_clip(dest, &oldclip);
+    else
+        set_clip(dest, NULL);
+
+    return box;
+}
+
+FC_Rect FC_DrawBoxEffect(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Effect effect, const char* formatted_text, ...)
+{
+    Uint8 useClip;
+    if(formatted_text == NULL || font == NULL)
+        return FC_MakeRect(box.x, box.y, 0, 0);
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    useClip = has_clip(dest);
+    FC_Rect oldclip, newclip;
+    if(useClip)
+    {
+        oldclip = get_clip(dest);
+        newclip = FC_RectIntersect(oldclip, box);
+    }
+    else
+        newclip = box;
+    set_clip(dest, &newclip);
+
+    set_color_for_all_caches(font, effect.color);
+
+    FC_DrawColumnFromBuffer(font, dest, box, NULL, effect.scale, effect.alignment);
+
+    if(useClip)
+        set_clip(dest, &oldclip);
+    else
+        set_clip(dest, NULL);
+
+    return box;
+}
+
+FC_Rect FC_DrawColumn(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, const char* formatted_text, ...)
+{
+    FC_Rect box = {x, y, width, 0};
+    int total_height;
+
+    if(formatted_text == NULL || font == NULL)
+        return FC_MakeRect(x, y, 0, 0);
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    set_color_for_all_caches(font, font->default_color);
+
+    FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), FC_ALIGN_LEFT);
+
+    return FC_MakeRect(box.x, box.y, width, total_height);
+}
+
+FC_Rect FC_DrawColumnAlign(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_AlignEnum align, const char* formatted_text, ...)
+{
+    FC_Rect box = {x, y, width, 0};
+    int total_height;
+
+    if(formatted_text == NULL || font == NULL)
+        return FC_MakeRect(x, y, 0, 0);
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    set_color_for_all_caches(font, font->default_color);
+
+    switch(align)
+    {
+    case FC_ALIGN_CENTER:
+        box.x -= width/2;
+        break;
+    case FC_ALIGN_RIGHT:
+        box.x -= width;
+        break;
+    default:
+        break;
+    }
+
+    FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), align);
+
+    return FC_MakeRect(box.x, box.y, width, total_height);
+}
+
+FC_Rect FC_DrawColumnScale(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Scale scale, const char* formatted_text, ...)
+{
+    FC_Rect box = {x, y, width, 0};
+    int total_height;
+
+    if(formatted_text == NULL || font == NULL)
+        return FC_MakeRect(x, y, 0, 0);
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    set_color_for_all_caches(font, font->default_color);
+
+    FC_DrawColumnFromBuffer(font, dest, box, &total_height, scale, FC_ALIGN_LEFT);
+
+    return FC_MakeRect(box.x, box.y, width, total_height);
+}
+
+FC_Rect FC_DrawColumnColor(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, SDL_Color color, const char* formatted_text, ...)
+{
+    FC_Rect box = {x, y, width, 0};
+    int total_height;
+
+    if(formatted_text == NULL || font == NULL)
+        return FC_MakeRect(x, y, 0, 0);
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    set_color_for_all_caches(font, color);
+
+    FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), FC_ALIGN_LEFT);
+
+    return FC_MakeRect(box.x, box.y, width, total_height);
+}
+
+FC_Rect FC_DrawColumnEffect(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Effect effect, const char* formatted_text, ...)
+{
+    FC_Rect box = {x, y, width, 0};
+    int total_height;
+
+    if(formatted_text == NULL || font == NULL)
+        return FC_MakeRect(x, y, 0, 0);
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    set_color_for_all_caches(font, effect.color);
+
+    switch(effect.alignment)
+    {
+    case FC_ALIGN_CENTER:
+        box.x -= width/2;
+        break;
+    case FC_ALIGN_RIGHT:
+        box.x -= width;
+        break;
+    default:
+        break;
+    }
+
+    FC_DrawColumnFromBuffer(font, dest, box, &total_height, effect.scale, effect.alignment);
+
+    return FC_MakeRect(box.x, box.y, width, total_height);
+}
+
+static FC_Rect FC_RenderCenter(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text)
+{
+    FC_Rect result = {x, y, 0, 0};
+    if(text == NULL || font == NULL)
+        return result;
+
+    char* str = U8_strdup(text);
+    char* del = str;
+    char* c;
+
+    // Go through str, when you find a \n, replace it with \0 and print it
+    // then move down, back, and continue.
+    for(c = str; *c != '\0';)
+    {
+        if(*c == '\n')
+        {
+            *c = '\0';
+            result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str)/2.0f, y, scale, str), result);
+            *c = '\n';
+            c++;
+            str = c;
+            y += scale.y*font->height;
+        }
+        else
+            c++;
+    }
+
+    result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str)/2.0f, y, scale, str), result);
+
+    free(del);
+    return result;
+}
+
+static FC_Rect FC_RenderRight(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text)
+{
+    FC_Rect result = {x, y, 0, 0};
+    if(text == NULL || font == NULL)
+        return result;
+
+    char* str = U8_strdup(text);
+    char* del = str;
+    char* c;
+
+    for(c = str; *c != '\0';)
+    {
+        if(*c == '\n')
+        {
+            *c = '\0';
+            result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str), y, scale, str), result);
+            *c = '\n';
+            c++;
+            str = c;
+            y += scale.y*font->height;
+        }
+        else
+            c++;
+    }
+
+    result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str), y, scale, str), result);
+
+    free(del);
+    return result;
+}
+
+
+
+FC_Rect FC_DrawScale(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* formatted_text, ...)
+{
+    if(formatted_text == NULL || font == NULL)
+        return FC_MakeRect(x, y, 0, 0);
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    set_color_for_all_caches(font, font->default_color);
+
+    return FC_RenderLeft(font, dest, x, y, scale, fc_buffer);
+}
+
+FC_Rect FC_DrawAlign(FC_Font* font, FC_Target* dest, float x, float y, FC_AlignEnum align, const char* formatted_text, ...)
+{
+    if(formatted_text == NULL || font == NULL)
+        return FC_MakeRect(x, y, 0, 0);
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    set_color_for_all_caches(font, font->default_color);
+
+    FC_Rect result;
+    switch(align)
+    {
+        case FC_ALIGN_LEFT:
+            result = FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer);
+            break;
+        case FC_ALIGN_CENTER:
+            result = FC_RenderCenter(font, dest, x, y, FC_MakeScale(1,1), fc_buffer);
+            break;
+        case FC_ALIGN_RIGHT:
+            result = FC_RenderRight(font, dest, x, y, FC_MakeScale(1,1), fc_buffer);
+            break;
+        default:
+            result = FC_MakeRect(x, y, 0, 0);
+            break;
+    }
+
+    return result;
+}
+
+FC_Rect FC_DrawColor(FC_Font* font, FC_Target* dest, float x, float y, SDL_Color color, const char* formatted_text, ...)
+{
+    if(formatted_text == NULL || font == NULL)
+        return FC_MakeRect(x, y, 0, 0);
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    set_color_for_all_caches(font, color);
+
+    return FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer);
+}
+
+
+FC_Rect FC_DrawEffect(FC_Font* font, FC_Target* dest, float x, float y, FC_Effect effect, const char* formatted_text, ...)
+{
+    if(formatted_text == NULL || font == NULL)
+        return FC_MakeRect(x, y, 0, 0);
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    set_color_for_all_caches(font, effect.color);
+
+    FC_Rect result;
+    switch(effect.alignment)
+    {
+        case FC_ALIGN_LEFT:
+            result = FC_RenderLeft(font, dest, x, y, effect.scale, fc_buffer);
+            break;
+        case FC_ALIGN_CENTER:
+            result = FC_RenderCenter(font, dest, x, y, effect.scale, fc_buffer);
+            break;
+        case FC_ALIGN_RIGHT:
+            result = FC_RenderRight(font, dest, x, y, effect.scale, fc_buffer);
+            break;
+        default:
+            result = FC_MakeRect(x, y, 0, 0);
+            break;
+    }
+
+    return result;
+}
+
+
+
+
+// Getters
+
+
+FC_FilterEnum FC_GetFilterMode(FC_Font* font)
+{
+    if(font == NULL)
+        return FC_FILTER_NEAREST;
+
+    return font->filter;
+}
+
+Uint16 FC_GetLineHeight(FC_Font* font)
+{
+    if(font == NULL)
+        return 0;
+
+    return font->height;
+}
+
+Uint16 FC_GetHeight(FC_Font* font, const char* formatted_text, ...)
+{
+    if(formatted_text == NULL || font == NULL)
+        return 0;
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    Uint16 numLines = 1;
+    const char* c;
+
+    for (c = fc_buffer; *c != '\0'; c++)
+    {
+        if(*c == '\n')
+            numLines++;
+    }
+
+    //   Actual height of letter region + line spacing
+    return font->height*numLines + font->lineSpacing*(numLines - 1);  //height*numLines;
+}
+
+Uint16 FC_GetWidth(FC_Font* font, const char* formatted_text, ...)
+{
+    if(formatted_text == NULL || font == NULL)
+        return 0;
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    const char* c;
+    Uint16 width = 0;
+    Uint16 bigWidth = 0;  // Allows for multi-line strings
+
+    for (c = fc_buffer; *c != '\0'; c++)
+    {
+        if(*c == '\n')
+        {
+            bigWidth = bigWidth >= width? bigWidth : width;
+            width = 0;
+            continue;
+        }
+
+        FC_GlyphData glyph;
+        Uint32 codepoint = FC_GetCodepointFromUTF8(&c, 1);
+        if(FC_GetGlyphData(font, &glyph, codepoint) || FC_GetGlyphData(font, &glyph, ' '))
+            width += glyph.rect.w;
+    }
+    bigWidth = bigWidth >= width? bigWidth : width;
+
+    return bigWidth;
+}
+
+// If width == -1, use no width limit
+FC_Rect FC_GetCharacterOffset(FC_Font* font, Uint16 position_index, int column_width, const char* formatted_text, ...)
+{
+    FC_Rect result = {0, 0, 1, FC_GetLineHeight(font)};
+    FC_StringList *ls, *iter;
+    int num_lines = 0;
+    Uint8 done = 0;
+
+    if(formatted_text == NULL || column_width == 0 || position_index == 0 || font == NULL)
+        return result;
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    ls = FC_GetBufferFitToColumn(font, column_width, FC_MakeScale(1,1), 1);
+    for(iter = ls; iter != NULL;)
+    {
+        char* line;
+        int i = 0;
+        FC_StringList* next_iter = iter->next;
+
+        ++num_lines;
+        for(line = iter->value; line != NULL && *line != '\0'; line = (char*)U8_next(line))
+        {
+            ++i;
+            --position_index;
+            if(position_index == 0)
+            {
+                // FIXME: Doesn't handle box-wrapped newlines correctly
+                line = (char*)U8_next(line);
+                line[0] = '\0';
+                result.x = FC_GetWidth(font, "%s", iter->value);
+                done = 1;
+                break;
+            }
+        }
+        if(done)
+            break;
+
+        // Prevent line wrapping if there are no more lines
+        if(next_iter == NULL && !done)
+            result.x = FC_GetWidth(font, "%s", iter->value);
+        iter = next_iter;
+    }
+    FC_StringListFree(ls);
+
+    if(num_lines > 1)
+    {
+        result.y = (num_lines - 1) * FC_GetLineHeight(font);
+    }
+
+    return result;
+}
+
+
+Uint16 FC_GetColumnHeight(FC_Font* font, Uint16 width, const char* formatted_text, ...)
+{
+    int y = 0;
+
+    FC_StringList *ls, *iter;
+
+    if(font == NULL)
+        return 0;
+
+    if(formatted_text == NULL || width == 0)
+        return font->height;
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    ls = FC_GetBufferFitToColumn(font, width, FC_MakeScale(1,1), 0);
+    for(iter = ls; iter != NULL; iter = iter->next)
+    {
+        y += FC_GetLineHeight(font);
+    }
+    FC_StringListFree(ls);
+
+    return y;
+}
+
+static int FC_GetAscentFromCodepoint(FC_Font* font, Uint32 codepoint)
+{
+    FC_GlyphData glyph;
+
+    if(font == NULL)
+        return 0;
+
+    // FIXME: Store ascent so we can return it here
+    FC_GetGlyphData(font, &glyph, codepoint);
+    return glyph.rect.h;
+}
+
+static int FC_GetDescentFromCodepoint(FC_Font* font, Uint32 codepoint)
+{
+    FC_GlyphData glyph;
+
+    if(font == NULL)
+        return 0;
+
+    // FIXME: Store descent so we can return it here
+    FC_GetGlyphData(font, &glyph, codepoint);
+    return glyph.rect.h;
+}
+
+int FC_GetAscent(FC_Font* font, const char* formatted_text, ...)
+{
+    Uint32 codepoint;
+    int max, ascent;
+    const char* c;
+
+    if(font == NULL)
+        return 0;
+
+    if(formatted_text == NULL)
+        return font->ascent;
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    max = 0;
+    c = fc_buffer;
+
+    while(*c != '\0')
+    {
+        codepoint = FC_GetCodepointFromUTF8(&c, 1);
+        if(codepoint != 0)
+        {
+            ascent = FC_GetAscentFromCodepoint(font, codepoint);
+            if(ascent > max)
+                max = ascent;
+        }
+        ++c;
+    }
+    return max;
+}
+
+int FC_GetDescent(FC_Font* font, const char* formatted_text, ...)
+{
+    Uint32 codepoint;
+    int max, descent;
+    const char* c;
+
+    if(font == NULL)
+        return 0;
+
+    if(formatted_text == NULL)
+        return font->descent;
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    max = 0;
+    c = fc_buffer;
+
+    while(*c != '\0')
+    {
+        codepoint = FC_GetCodepointFromUTF8(&c, 1);
+        if(codepoint != 0)
+        {
+            descent = FC_GetDescentFromCodepoint(font, codepoint);
+            if(descent > max)
+                max = descent;
+        }
+        ++c;
+    }
+    return max;
+}
+
+int FC_GetBaseline(FC_Font* font)
+{
+    if(font == NULL)
+        return 0;
+
+    return font->baseline;
+}
+
+int FC_GetSpacing(FC_Font* font)
+{
+    if(font == NULL)
+        return 0;
+
+    return font->letterSpacing;
+}
+
+int FC_GetLineSpacing(FC_Font* font)
+{
+    if(font == NULL)
+        return 0;
+
+    return font->lineSpacing;
+}
+
+Uint16 FC_GetMaxWidth(FC_Font* font)
+{
+    if(font == NULL)
+        return 0;
+
+    return font->maxWidth;
+}
+
+SDL_Color FC_GetDefaultColor(FC_Font* font)
+{
+    if(font == NULL)
+    {
+        SDL_Color c = {0,0,0,255};
+        return c;
+    }
+
+    return font->default_color;
+}
+
+FC_Rect FC_GetBounds(FC_Font* font, float x, float y, FC_AlignEnum align, FC_Scale scale, const char* formatted_text, ...)
+{
+    FC_Rect result = {x, y, 0, 0};
+
+    if(formatted_text == NULL)
+        return result;
+
+    // Create a temp buffer while GetWidth and GetHeight use fc_buffer.
+    char* temp = (char*)malloc(fc_buffer_size);
+    FC_EXTRACT_VARARGS(temp, formatted_text);
+
+    result.w = FC_GetWidth(font, "%s", temp) * scale.x;
+    result.h = FC_GetHeight(font, "%s", temp) * scale.y;
+
+    switch(align)
+    {
+        case FC_ALIGN_LEFT:
+            break;
+        case FC_ALIGN_CENTER:
+            result.x -= result.w/2;
+            break;
+        case FC_ALIGN_RIGHT:
+            result.x -= result.w;
+            break;
+        default:
+            break;
+    }
+
+    free(temp);
+
+    return result;
+}
+
+Uint8 FC_InRect(float x, float y, FC_Rect input_rect)
+{
+    return (input_rect.x <= x && x <= input_rect.x + input_rect.w && input_rect.y <= y && y <= input_rect.y + input_rect.h);
+}
+
+// TODO: Make it work with alignment
+Uint16 FC_GetPositionFromOffset(FC_Font* font, float x, float y, int column_width, FC_AlignEnum align, const char* formatted_text, ...)
+{
+    FC_StringList *ls, *iter;
+    Uint8 done = 0;
+    int height = FC_GetLineHeight(font);
+    Uint16 position = 0;
+    int current_x = 0;
+    int current_y = 0;
+    FC_GlyphData glyph_data;
+
+    if(formatted_text == NULL || column_width == 0 || font == NULL)
+        return 0;
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    ls = FC_GetBufferFitToColumn(font, column_width, FC_MakeScale(1,1), 1);
+    for(iter = ls; iter != NULL; iter = iter->next)
+    {
+        char* line;
+
+        for(line = iter->value; line != NULL && *line != '\0'; line = (char*)U8_next(line))
+        {
+            if(FC_GetGlyphData(font, &glyph_data, FC_GetCodepointFromUTF8((const char**)&line, 0)))
+            {
+                if(FC_InRect(x, y, FC_MakeRect(current_x, current_y, glyph_data.rect.w, glyph_data.rect.h)))
+                {
+                    done = 1;
+                    break;
+                }
+
+                current_x += glyph_data.rect.w;
+            }
+            position++;
+        }
+        if(done)
+            break;
+
+        current_x = 0;
+        current_y += height;
+        if(y < current_y)
+            break;
+    }
+    FC_StringListFree(ls);
+
+    return position;
+}
+
+int FC_GetWrappedText(FC_Font* font, char* result, int max_result_size, Uint16 width, const char* formatted_text, ...)
+{
+    FC_StringList *ls, *iter;
+
+    if(font == NULL)
+        return 0;
+
+    if(formatted_text == NULL || width == 0)
+        return 0;
+
+    FC_EXTRACT_VARARGS(fc_buffer, formatted_text);
+
+    ls = FC_GetBufferFitToColumn(font, width, FC_MakeScale(1,1), 0);
+    int size_so_far = 0;
+    int size_remaining = max_result_size-1; // reserve for \0
+    for(iter = ls; iter != NULL && size_remaining > 0; iter = iter->next)
+    {
+        // Copy as much of this line as we can
+        int len = strlen(iter->value);
+        int num_bytes = FC_MIN(len, size_remaining);
+        memcpy(&result[size_so_far], iter->value, num_bytes);
+        size_so_far += num_bytes;
+
+        // If there's another line, add newline character
+        if(size_remaining > 0 && iter->next != NULL)
+        {
+            --size_remaining;
+            result[size_so_far] = '\n';
+            ++size_so_far;
+        }
+    }
+    FC_StringListFree(ls);
+
+    result[size_so_far] = '\0';
+
+    return size_so_far;
+}
+
+
+
+// Setters
+
+
+void FC_SetFilterMode(FC_Font* font, FC_FilterEnum filter)
+{
+    if(font == NULL)
+        return;
+
+    if(font->filter != filter)
+    {
+        font->filter = filter;
+
+        #ifdef FC_USE_SDL_GPU
+        // Update each texture to use this filter mode
+        {
+            int i;
+            GPU_FilterEnum gpu_filter = GPU_FILTER_NEAREST;
+            if(FC_GetFilterMode(font) == FC_FILTER_LINEAR)
+                gpu_filter = GPU_FILTER_LINEAR;
+
+            for(i = 0; i < font->glyph_cache_count; ++i)
+            {
+                GPU_SetImageFilter(font->glyph_cache[i], gpu_filter);
+            }
+        }
+        #endif
+    }
+}
+
+
+void FC_SetSpacing(FC_Font* font, int LetterSpacing)
+{
+    if(font == NULL)
+        return;
+
+    font->letterSpacing = LetterSpacing;
+}
+
+void FC_SetLineSpacing(FC_Font* font, int LineSpacing)
+{
+    if(font == NULL)
+        return;
+
+    font->lineSpacing = LineSpacing;
+}
+
+void FC_SetDefaultColor(FC_Font* font, SDL_Color color)
+{
+    if(font == NULL)
+        return;
+
+    font->default_color = color;
+}
diff --git a/src/app.cpp b/src/app.cpp
index b9efeb8327d5a91c833d30a1baf88175a2309d78..a4dddc903751c29eac72ac94dc4065f45e0d1712 100644
--- a/src/app.cpp
+++ b/src/app.cpp
@@ -51,17 +51,10 @@ namespace uwe {
             update();
             
             draw();
-
-            // copy framebuffer pixels to GU texture for rendering
-            SDL_UpdateTexture(
-                context_.get_texture(),
-                NULL,
-                context_.get_pixels().data(),
-                context_.get_tex_width() * 4);
-
-            SDL_RenderCopy( context_.get_renderer(), context_.get_texture(), NULL, NULL );
-            SDL_RenderPresent( context_.get_renderer() );
             
+            // finally present the renderer to the GPU
+            SDL_RenderPresent( context_.get_renderer() );
+
             frames++;
             if (output_framerate_) {
                 const uint64_t end = SDL_GetPerformanceCounter();
diff --git a/src/context.cpp b/src/context.cpp
index 035143cb7a79752f23a70cf153a194d4958f68a9..f9dc0bb2deab97219682395b9c2891d35a4f3720 100644
--- a/src/context.cpp
+++ b/src/context.cpp
@@ -36,6 +36,9 @@ namespace uwe {
     }
 
     Context::~Context() {
+        for (auto font: loaded_fonts_) {
+            FC_FreeFont(font);
+        }
         SDL_DestroyRenderer( renderer_ );
         SDL_DestroyWindow( window_ );
         SDL_Quit();
diff --git a/windows/lib/freetype.lib b/windows/lib/freetype.lib
new file mode 100644
index 0000000000000000000000000000000000000000..22b37a50542a0541574a5f61f450616b874088da
Binary files /dev/null and b/windows/lib/freetype.lib differ
diff --git a/windows/lib/x64/SDL2_image.lib b/windows/lib/x64/SDL2_image.lib
new file mode 100755
index 0000000000000000000000000000000000000000..6d00aed6db965856cea7c53bb15df7462c90b6e9
Binary files /dev/null and b/windows/lib/x64/SDL2_image.lib differ
diff --git a/zlib1.dll b/zlib1.dll
new file mode 100644
index 0000000000000000000000000000000000000000..e7493de315264b254bdf914737274034e9017598
Binary files /dev/null and b/zlib1.dll differ