text: //
  // Copyright (c) 2009-2013 Mikko Mononen memon@inside.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.
  //
  
  #ifndef FONS_H
  #define FONS_H
  
  #define FONS_INVALID -1
  
  enum FONSflags
  {
    FONS_ZERO_TOPLEFT = 1,
    FONS_ZERO_BOTTOMLEFT = 2,
  };
  
  enum FONSalign
  {
    // Horizontal align
    FONS_ALIGN_LEFT = 1 << 0, // Default
    FONS_ALIGN_CENTER = 1 << 1,
    FONS_ALIGN_RIGHT = 1 << 2,
    // Vertical align
    FONS_ALIGN_TOP = 1 << 3,
    FONS_ALIGN_MIDDLE = 1 << 4,
    FONS_ALIGN_BOTTOM = 1 << 5,
    FONS_ALIGN_BASELINE = 1 << 6, // Default
  };
  
  enum FONSglyphBitmap
  {
    FONS_GLYPH_BITMAP_OPTIONAL = 1,
    FONS_GLYPH_BITMAP_REQUIRED = 2,
  };
  
  enum FONSerrorCode
  {
    // Font atlas is full.
    FONS_ATLAS_FULL = 1,
    // Scratch memory used to render glyphs is full, requested size reported in
    // 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE.
    FONS_SCRATCH_FULL = 2,
    // Calls to fonsPushState has created too large stack, if you need deep state
    // stack bump up FONS_MAX_STATES.
    FONS_STATES_OVERFLOW = 3,
    // Trying to pop too many states fonsPopState().
    FONS_STATES_UNDERFLOW = 4,
  };
  
  struct FONSparams
  {
    int width, height;
    unsigned char flags;
    void* userPtr;
    int (*renderCreate)(void* uptr, int width, int height);
    int (*renderResize)(void* uptr, int width, int height);
    void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data);
    void (*renderDraw)(void* uptr,
                       const float* verts,
                       const float* tcoords,
                       const unsigned int* colors,
                       int nverts);
    void (*renderDelete)(void* uptr);
  };
  typedef struct FONSparams FONSparams;
  
  struct FONSquad
  {
    float x0, y0, s0, t0;
    float x1, y1, s1, t1;
  };
  typedef struct FONSquad FONSquad;
  
  struct FONStextIter
  {
    float x, y, nextx, nexty, scale, spacing;
    unsigned int codepoint;
    short isize, iblur;
    struct FONSfont* font;
    int prevGlyphIndex;
    const char* str;
    const char* next;
    const char* end;
    unsigned int utf8state;
    int bitmapOption;
  };
  typedef struct FONStextIter FONStextIter;
  
  typedef struct FONScontext FONScontext;
  
  // Constructor and destructor.
  FONScontext*
  fonsCreateInternal(FONSparams* params);
  void
  fonsDeleteInternal(FONScontext* s);
  
  void
  fonsSetErrorCallback(FONScontext* s,
                       void (*callback)(void* uptr, int error, int val),
                       void* uptr);
  // Returns current atlas size.
  void
  fonsGetAtlasSize(FONScontext* s, int* width, int* height);
  // Expands the atlas size.
  int
  fonsExpandAtlas(FONScontext* s, int width, int height);
  // Resets the whole stash.
  int
  fonsResetAtlas(FONScontext* stash, int width, int height);
  
  // Add fonts
  int
  fonsAddFont(FONScontext* s, const char* name, const char* path);
  int
  fonsAddFontMem(FONScontext* s,
                 const char* name,
                 unsigned char* data,
                 int ndata,
                 int freeData);
  int
  fonsGetFontByName(FONScontext* s, const char* name);
  
  // State handling
  void
  fonsPushState(FONScontext* s);
  void
  fonsPopState(FONScontext* s);
  void
  fonsClearState(FONScontext* s);
  
  // State setting
  void
  fonsSetSize(FONScontext* s, float size);
  void
  fonsSetColor(FONScontext* s, unsigned int color);
  void
  fonsSetSpacing(FONScontext* s, float spacing);
  void
  fonsSetBlur(FONScontext* s, float blur);
  void
  fonsSetAlign(FONScontext* s, int align);
  void
  fonsSetFont(FONScontext* s, int font);
  
  // Draw text
  float
  fonsDrawText(FONScontext* s,
               float x,
               float y,
               const char* string,
               const char* end);
  
  // Measure text
  float
  fonsTextBounds(FONScontext* s,
                 float x,
                 float y,
                 const char* string,
                 const char* end,
                 float* bounds);
  void
  fonsLineBounds(FONScontext* s, float y, float* miny, float* maxy);
  void
  fonsVertMetrics(FONScontext* s,
                  float* ascender,
                  float* descender,
                  float* lineh);
  
  // Text iterator
  int
  fonsTextIterInit(FONScontext* stash,
                   FONStextIter* iter,
                   float x,
                   float y,
                   const char* str,
                   const char* end,
                   int bitmapOption);
  int
  fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad);
  
  // Pull texture changes
  const unsigned char*
  fonsGetTextureData(FONScontext* stash, int* width, int* height);
  int
  fonsValidateTexture(FONScontext* s, int* dirty);
  
  // Draws the stash texture for debugging
  void
  fonsDrawDebug(FONScontext* s, float x, float y);
  
  #endif // FONTSTASH_H
  
  #ifdef FONTSTASH_IMPLEMENTATION
  
  #define FONS_NOTUSED(v) (void)sizeof(v)
  
  #ifdef FONS_USE_FREETYPE
  
  #include <ft2build.h>
  #include FT_FREETYPE_H
  #include FT_ADVANCES_H
  #include <math.h>
  
  struct FONSttFontImpl
  {
    FT_Face font;
  };
  typedef struct FONSttFontImpl FONSttFontImpl;
  
  static FT_Library ftLibrary;
  
  int
  fons__tt_init(FONScontext* context)
  {
    FT_Error ftError;
    FONS_NOTUSED(context);
    ftError = FT_Init_FreeType(&ftLibrary);
    return ftError == 0;
  }
  
  int
  fons__tt_loadFont(FONScontext* context,
                    FONSttFontImpl* font,
                    unsigned char* data,
                    int dataSize)
  {
    FT_Error ftError;
    FONS_NOTUSED(context);
  
    // font->font.userdata = stash;
    ftError = FT_New_Memory_Face(
      ftLibrary, (const FT_Byte*)data, dataSize, 0, &font->font);
    return ftError == 0;
  }
  
  void
  fons__tt_getFontVMetrics(FONSttFontImpl* font,
                           int* ascent,
                           int* descent,
                           int* lineGap)
  {
    *ascent = font->font->ascender;
    *descent = font->font->descender;
    *lineGap = font->font->height - (*ascent - *descent);
  }
  
  float
  fons__tt_getPixelHeightScale(FONSttFontImpl* font, float size)
  {
    return size / (font->font->ascender - font->font->descender);
  }
  
  int
  fons__tt_getGlyphIndex(FONSttFontImpl* font, int codepoint)
  {
    return FT_Get_Char_Index(font->font, codepoint);
  }
  
  int
  fons__tt_buildGlyphBitmap(FONSttFontImpl* font,
                            int glyph,
                            float size,
                            float scale,
                            int* advance,
                            int* lsb,
                            int* x0,
                            int* y0,
                            int* x1,
                            int* y1)
  {
    FT_Error ftError;
    FT_GlyphSlot ftGlyph;
    FT_Fixed advFixed;
    FONS_NOTUSED(scale);
  
    ftError = FT_Set_Pixel_Sizes(
      font->font,
      0,
      (FT_UInt)(size * (float)font->font->units_per_EM /
                (float)(font->font->ascender - font->font->descender)));
    if (ftError)
      return 0;
    ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER);
    if (ftError)
      return 0;
    ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, &advFixed);
    if (ftError)
      return 0;
    ftGlyph = font->font->glyph;
    *advance = (int)advFixed;
    *lsb = (int)ftGlyph->metrics.horiBearingX;
    *x0 = ftGlyph->bitmap_left;
    *x1 = *x0 + ftGlyph->bitmap.width;
    *y0 = -ftGlyph->bitmap_top;
    *y1 = *y0 + ftGlyph->bitmap.rows;
    return 1;
  }
  
  void
  fons__tt_renderGlyphBitmap(FONSttFontImpl* font,
                             unsigned char* output,
                             int outWidth,
                             int outHeight,
                             int outStride,
                             float scaleX,
                             float scaleY,
                             int glyph)
  {
    FT_GlyphSlot ftGlyph = font->font->glyph;
    int ftGlyphOffset = 0;
    int x, y;
    FONS_NOTUSED(outWidth);
    FONS_NOTUSED(outHeight);
    FONS_NOTUSED(scaleX);
    FONS_NOTUSED(scaleY);
    FONS_NOTUSED(
      glyph); // glyph has already been loaded by fons__tt_buildGlyphBitmap
  
    for (y = 0; y < ftGlyph->bitmap.rows; y++) {
      for (x = 0; x < ftGlyph->bitmap.width; x++) {
        output[(y * outStride) + x] = ftGlyph->bitmap.buffer[ftGlyphOffset++];
      }
    }
  }
  
  int
  fons__tt_getGlyphKernAdvance(FONSttFontImpl* font, int glyph1, int glyph2)
  {
    FT_Vector ftKerning;
    FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning);
    return (int)((ftKerning.x + 32) >> 6); // Round up and convert to integer
  }
  
  #else
  
  #define STB_TRUETYPE_IMPLEMENTATION
  static void*
  fons__tmpalloc(size_t size, void* up);
  static void
  fons__tmpfree(void* ptr, void* up);
  #define STBTT_malloc(x, u) fons__tmpalloc(x, u)
  #define STBTT_free(x, u) fons__tmpfree(x, u)
  #include "stb_truetype.h"
  
  struct FONSttFontImpl
  {
    stbtt_fontinfo font;
  };
  typedef struct FONSttFontImpl FONSttFontImpl;
  
  int
  fons__tt_init(FONScontext* context)
  {
    FONS_NOTUSED(context);
    return 1;
  }
  
  int
  fons__tt_loadFont(FONScontext* context,
                    FONSttFontImpl* font,
                    unsigned char* data,
                    int dataSize)
  {
    int stbError;
    FONS_NOTUSED(dataSize);
  
    font->font.userdata = context;
    stbError = stbtt_InitFont(&font->font, data, 0);
    return stbError;
  }
  
  void
  fons__tt_getFontVMetrics(FONSttFontImpl* font,
                           int* ascent,
                           int* descent,
                           int* lineGap)
  {
    stbtt_GetFontVMetrics(&font->font, ascent, descent, lineGap);
  }
  
  float
  fons__tt_getPixelHeightScale(FONSttFontImpl* font, float size)
  {
    return stbtt_ScaleForPixelHeight(&font->font, size);
  }
  
  int
  fons__tt_getGlyphIndex(FONSttFontImpl* font, int codepoint)
  {
    return stbtt_FindGlyphIndex(&font->font, codepoint);
  }
  
  int
  fons__tt_buildGlyphBitmap(FONSttFontImpl* font,
                            int glyph,
                            float size,
                            float scale,
                            int* advance,
                            int* lsb,
                            int* x0,
                            int* y0,
                            int* x1,
                            int* y1)
  {
    FONS_NOTUSED(size);
    stbtt_GetGlyphHMetrics(&font->font, glyph, advance, lsb);
    stbtt_GetGlyphBitmapBox(&font->font, glyph, scale, scale, x0, y0, x1, y1);
    return 1;
  }
  
  void
  fons__tt_renderGlyphBitmap(FONSttFontImpl* font,
                             unsigned char* output,
                             int outWidth,
                             int outHeight,
                             int outStride,
                             float scaleX,
                             float scaleY,
                             int glyph)
  {
    stbtt_MakeGlyphBitmap(
      &font->font, output, outWidth, outHeight, outStride, scaleX, scaleY, glyph);
  }
  
  int
  fons__tt_getGlyphKernAdvance(FONSttFontImpl* font, int glyph1, int glyph2)
  {
    return stbtt_GetGlyphKernAdvance(&font->font, glyph1, glyph2);
  }
  
  #endif
  
  #ifndef FONS_SCRATCH_BUF_SIZE
  #define FONS_SCRATCH_BUF_SIZE 64000
  #endif
  #ifndef FONS_HASH_LUT_SIZE
  #define FONS_HASH_LUT_SIZE 256
  #endif
  #ifndef FONS_INIT_FONTS
  #define FONS_INIT_FONTS 4
  #endif
  #ifndef FONS_INIT_GLYPHS
  #define FONS_INIT_GLYPHS 256
  #endif
  #ifndef FONS_INIT_ATLAS_NODES
  #define FONS_INIT_ATLAS_NODES 256
  #endif
  #ifndef FONS_VERTEX_COUNT
  #define FONS_VERTEX_COUNT 1024
  #endif
  #ifndef FONS_MAX_STATES
  #define FONS_MAX_STATES 20
  #endif
  #ifndef FONS_MAX_FALLBACKS
  #define FONS_MAX_FALLBACKS 20
  #endif
  
  static unsigned int
  fons__hashint(unsigned int a)
  {
    a += ~(a << 15);
    a ^= (a >> 10);
    a += (a << 3);
    a ^= (a >> 6);
    a += ~(a << 11);
    a ^= (a >> 16);
    return a;
  }
  
  static int
  fons__mini(int a, int b)
  {
    return a < b ? a : b;
  }
  
  static int
  fons__maxi(int a, int b)
  {
    return a > b ? a : b;
  }
  
  struct FONSglyph
  {
    unsigned int codepoint;
    int index;
    int next;
    short size, blur;
    short x0, y0, x1, y1;
    short xadv, xoff, yoff;
  };
  typedef struct FONSglyph FONSglyph;
  
  struct FONSfont
  {
    FONSttFontImpl font;
    char name[64];
    unsigned char* data;
    int dataSize;
    unsigned char freeData;
    float ascender;
    float descender;
    float lineh;
    FONSglyph* glyphs;
    int cglyphs;
    int nglyphs;
    int lut[FONS_HASH_LUT_SIZE];
    int fallbacks[FONS_MAX_FALLBACKS];
    int nfallbacks;
  };
  typedef struct FONSfont FONSfont;
  
  struct FONSstate
  {
    int font;
    int align;
    float size;
    unsigned int color;
    float blur;
    float spacing;
  };
  typedef struct FONSstate FONSstate;
  
  struct FONSatlasNode
  {
    short x, y, width;
  };
  typedef struct FONSatlasNode FONSatlasNode;
  
  struct FONSatlas
  {
    int width, height;
    FONSatlasNode* nodes;
    int nnodes;
    int cnodes;
  };
  typedef struct FONSatlas FONSatlas;
  
  struct FONScontext
  {
    FONSparams params;
    float itw, ith;
    unsigned char* texData;
    int dirtyRect[4];
    FONSfont** fonts;
    FONSatlas* atlas;
    int cfonts;
    int nfonts;
    float verts[FONS_VERTEX_COUNT * 2];
    float tcoords[FONS_VERTEX_COUNT * 2];
    unsigned int colors[FONS_VERTEX_COUNT];
    int nverts;
    unsigned char* scratch;
    int nscratch;
    FONSstate states[FONS_MAX_STATES];
    int nstates;
    void (*handleError)(void* uptr, int error, int val);
    void* errorUptr;
  };
  
  #ifdef STB_TRUETYPE_IMPLEMENTATION
  
  static void*
  fons__tmpalloc(size_t size, void* up)
  {
    unsigned char* ptr;
    FONScontext* stash = (FONScontext*)up;
  
    // 16-byte align the returned pointer
    size = (size + 0xf) & ~0xf;
  
    if (stash->nscratch + (int)size > FONS_SCRATCH_BUF_SIZE) {
      if (stash->handleError)
        stash->handleError(
          stash->errorUptr, FONS_SCRATCH_FULL, stash->nscratch + (int)size);
      return NULL;
    }
    ptr = stash->scratch + stash->nscratch;
    stash->nscratch += (int)size;
    return ptr;
  }
  
  static void
  fons__tmpfree(void* ptr, void* up)
  {
    (void)ptr;
    (void)up;
    // empty
  }
  
  #endif // STB_TRUETYPE_IMPLEMENTATION
  
  // Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
  // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
  
  #define FONS_UTF8_ACCEPT 0
  #define FONS_UTF8_REJECT 12
  
  static unsigned int
  fons__decutf8(unsigned int* state, unsigned int* codep, unsigned int byte)
  {
    static const unsigned char utf8d[] = {
      // The first part of the table maps bytes to character classes that
      // to reduce the size of the transition table and create bitmasks.
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      1,
      1,
      1,
      1,
      1,
      1,
      1,
      1,
      1,
      1,
      1,
      1,
      1,
      1,
      1,
      1,
      9,
      9,
      9,
      9,
      9,
      9,
      9,
      9,
      9,
      9,
      9,
      9,
      9,
      9,
      9,
      9,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      7,
      8,
      8,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      10,
      3,
      3,
      3,
      3,
      3,
      3,
      3,
      3,
      3,
      3,
      3,
      3,
      4,
      3,
      3,
      11,
      6,
      6,
      6,
      5,
      8,
      8,
      8,
      8,
      8,
      8,
      8,
      8,
      8,
      8,
      8,
  
      // The second part is a transition table that maps a combination
      // of a state of the automaton and a character class to a state.
      0,
      12,
      24,
      36,
      60,
      96,
      84,
      12,
      12,
      12,
      48,
      72,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
      0,
      12,
      12,
      12,
      12,
      12,
      0,
      12,
      0,
      12,
      12,
      12,
      24,
      12,
      12,
      12,
      12,
      12,
      24,
      12,
      24,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
      24,
      12,
      12,
      12,
      12,
      12,
      24,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
      24,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
      36,
      12,
      36,
      12,
      12,
      12,
      36,
      12,
      12,
      12,
      12,
      12,
      36,
      12,
      36,
      12,
      12,
      12,
      36,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
      12,
    };
  
    unsigned int type = utf8d[byte];
  
    *codep = (*state != FONS_UTF8_ACCEPT) ? (byte & 0x3fu) | (*codep << 6)
                                          : (0xff >> type) & (byte);
  
    *state = utf8d[256 + *state + type];
    return *state;
  }
  
  // Atlas based on Skyline Bin Packer by Jukka Jylänki
  
  static void
  fons__deleteAtlas(FONSatlas* atlas)
  {
    if (atlas == NULL)
      return;
    if (atlas->nodes != NULL)
      free(atlas->nodes);
    free(atlas);
  }
  
  static FONSatlas*
  fons__allocAtlas(int w, int h, int nnodes)
  {
    FONSatlas* atlas = NULL;
  
    // Allocate memory for the font stash.
    atlas = (FONSatlas*)malloc(sizeof(FONSatlas));
    if (atlas == NULL)
      goto error;
    memset(atlas, 0, sizeof(FONSatlas));
  
    atlas->width = w;
    atlas->height = h;
  
    // Allocate space for skyline nodes
    atlas->nodes = (FONSatlasNode*)malloc(sizeof(FONSatlasNode) * nnodes);
    if (atlas->nodes == NULL)
      goto error;
    memset(atlas->nodes, 0, sizeof(FONSatlasNode) * nnodes);
    atlas->nnodes = 0;
    atlas->cnodes = nnodes;
  
    // Init root node.
    atlas->nodes[0].x = 0;
    atlas->nodes[0].y = 0;
    atlas->nodes[0].width = (short)w;
    atlas->nnodes++;
  
    return atlas;
  
  error:
    if (atlas)
      fons__deleteAtlas(atlas);
    return NULL;
  }
  
  static int
  fons__atlasInsertNode(FONSatlas* atlas, int idx, int x, int y, int w)
  {
    int i;
    // Insert node
    if (atlas->nnodes + 1 > atlas->cnodes) {
      atlas->cnodes = atlas->cnodes == 0 ? 8 : atlas->cnodes * 2;
      atlas->nodes = (FONSatlasNode*)realloc(
        atlas->nodes, sizeof(FONSatlasNode) * atlas->cnodes);
      if (atlas->nodes == NULL)
        return 0;
    }
    for (i = atlas->nnodes; i > idx; i--)
      atlas->nodes[i] = atlas->nodes[i - 1];
    atlas->nodes[idx].x = (short)x;
    atlas->nodes[idx].y = (short)y;
    atlas->nodes[idx].width = (short)w;
    atlas->nnodes++;
  
    return 1;
  }
  
  static void
  fons__atlasRemoveNode(FONSatlas* atlas, int idx)
  {
    int i;
    if (atlas->nnodes == 0)
      return;
    for (i = idx; i < atlas->nnodes - 1; i++)
      atlas->nodes[i] = atlas->nodes[i + 1];
    atlas->nnodes--;
  }
  
  static void
  fons__atlasExpand(FONSatlas* atlas, int w, int h)
  {
    // Insert node for empty space
    if (w > atlas->width)
      fons__atlasInsertNode(
        atlas, atlas->nnodes, atlas->width, 0, w - atlas->width);
    atlas->width = w;
    atlas->height = h;
  }
  
  static void
  fons__atlasReset(FONSatlas* atlas, int w, int h)
  {
    atlas->width = w;
    atlas->height = h;
    atlas->nnodes = 0;
  
    // Init root node.
    atlas->nodes[0].x = 0;
    atlas->nodes[0].y = 0;
    atlas->nodes[0].width = (short)w;
    atlas->nnodes++;
  }
  
  static int
  fons__atlasAddSkylineLevel(FONSatlas* atlas,
                             int idx,
                             int x,
                             int y,
                             int w,
                             int h)
  {
    int i;
  
    // Insert new node
    if (fons__atlasInsertNode(atlas, idx, x, y + h, w) == 0)
      return 0;
  
    // Delete skyline segments that fall under the shadow of the new segment.
    for (i = idx + 1; i < atlas->nnodes; i++) {
      if (atlas->nodes[i].x < atlas->nodes[i - 1].x + atlas->nodes[i - 1].width) {
        int shrink =
          atlas->nodes[i - 1].x + atlas->nodes[i - 1].width - atlas->nodes[i].x;
        atlas->nodes[i].x += (short)shrink;
        atlas->nodes[i].width -= (short)shrink;
        if (atlas->nodes[i].width <= 0) {
          fons__atlasRemoveNode(atlas, i);
          i--;
        } else {
          break;
        }
      } else {
        break;
      }
    }
  
    // Merge same height skyline segments that are next to each other.
    for (i = 0; i < atlas->nnodes - 1; i++) {
      if (atlas->nodes[i].y == atlas->nodes[i + 1].y) {
        atlas->nodes[i].width += atlas->nodes[i + 1].width;
        fons__atlasRemoveNode(atlas, i + 1);
        i--;
      }
    }
  
    return 1;
  }
  
  static int
  fons__atlasRectFits(FONSatlas* atlas, int i, int w, int h)
  {
    // Checks if there is enough space at the location of skyline span 'i',
    // and return the max height of all skyline spans under that at that location,
    // (think tetris block being dropped at that position). Or -1 if no space
    // found.
    int x = atlas->nodes[i].x;
    int y = atlas->nodes[i].y;
    int spaceLeft;
    if (x + w > atlas->width)
      return -1;
    spaceLeft = w;
    while (spaceLeft > 0) {
      if (i == atlas->nnodes)
        return -1;
      y = fons__maxi(y, atlas->nodes[i].y);
      if (y + h > atlas->height)
        return -1;
      spaceLeft -= atlas->nodes[i].width;
      ++i;
    }
    return y;
  }
  
  static int
  fons__atlasAddRect(FONSatlas* atlas, int rw, int rh, int* rx, int* ry)
  {
    int besth = atlas->height, bestw = atlas->width, besti = -1;
    int bestx = -1, besty = -1, i;
  
    // Bottom left fit heuristic.
    for (i = 0; i < atlas->nnodes; i++) {
      int y = fons__atlasRectFits(atlas, i, rw, rh);
      if (y != -1) {
        if (y + rh < besth ||
            (y + rh == besth && atlas->nodes[i].width < bestw)) {
          besti = i;
          bestw = atlas->nodes[i].width;
          besth = y + rh;
          bestx = atlas->nodes[i].x;
          besty = y;
        }
      }
    }
  
    if (besti == -1)
      return 0;
  
    // Perform the actual packing.
    if (fons__atlasAddSkylineLevel(atlas, besti, bestx, besty, rw, rh) == 0)
      return 0;
  
    *rx = bestx;
    *ry = besty;
  
    return 1;
  }
  
  static void
  fons__addWhiteRect(FONScontext* stash, int w, int h)
  {
    int x, y, gx, gy;
    unsigned char* dst;
    if (fons__atlasAddRect(stash->atlas, w, h, &gx, &gy) == 0)
      return;
  
    // Rasterize
    dst = &stash->texData[gx + gy * stash->params.width];
    for (y = 0; y < h; y++) {
      for (x = 0; x < w; x++)
        dst[x] = 0xff;
      dst += stash->params.width;
    }
  
    stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], gx);
    stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], gy);
    stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], gx + w);
    stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], gy + h);
  }
  
  FONScontext*
  fonsCreateInternal(FONSparams* params)
  {
    FONScontext* stash = NULL;
  
    // Allocate memory for the font stash.
    stash = (FONScontext*)malloc(sizeof(FONScontext));
    if (stash == NULL)
      goto error;
    memset(stash, 0, sizeof(FONScontext));
  
    stash->params = *params;
  
    // Allocate scratch buffer.
    stash->scratch = (unsigned char*)malloc(FONS_SCRATCH_BUF_SIZE);
    if (stash->scratch == NULL)
      goto error;
  
    // Initialize implementation library
    if (!fons__tt_init(stash))
      goto error;
  
    if (stash->params.renderCreate != NULL) {
      if (stash->params.renderCreate(stash->params.userPtr,
                                     stash->params.width,
                                     stash->params.height) == 0)
        goto error;
    }
  
    stash->atlas = fons__allocAtlas(
      stash->params.width, stash->params.height, FONS_INIT_ATLAS_NODES);
    if (stash->atlas == NULL)
      goto error;
  
    // Allocate space for fonts.
    stash->fonts = (FONSfont**)malloc(sizeof(FONSfont*) * FONS_INIT_FONTS);
    if (stash->fonts == NULL)
      goto error;
    memset(stash->fonts, 0, sizeof(FONSfont*) * FONS_INIT_FONTS);
    stash->cfonts = FONS_INIT_FONTS;
    stash->nfonts = 0;
  
    // Create texture for the cache.
    stash->itw = 1.0f / stash->params.width;
    stash->ith = 1.0f / stash->params.height;
    stash->texData =
      (unsigned char*)malloc(stash->params.width * stash->params.height);
    if (stash->texData == NULL)
      goto error;
    memset(stash->texData, 0, stash->params.width * stash->params.height);
  
    stash->dirtyRect[0] = stash->params.width;
    stash->dirtyRect[1] = stash->params.height;
    stash->dirtyRect[2] = 0;
    stash->dirtyRect[3] = 0;
  
    // Add white rect at 0,0 for debug drawing.
    fons__addWhiteRect(stash, 2, 2);
  
    fonsPushState(stash);
    fonsClearState(stash);
  
    return stash;
  
  error:
    fonsDeleteInternal(stash);
    return NULL;
  }
  
  static FONSstate*
  fons__getState(FONScontext* stash)
  {
    return &stash->states[stash->nstates - 1];
  }
  
  int
  fonsAddFallbackFont(FONScontext* stash, int base, int fallback)
  {
    FONSfont* baseFont = stash->fonts[base];
    if (baseFont->nfallbacks < FONS_MAX_FALLBACKS) {
      baseFont->fallbacks[baseFont->nfallbacks++] = fallback;
      return 1;
    }
    return 0;
  }
  
  void
  fonsSetSize(FONScontext* stash, float size)
  {
    fons__getState(stash)->size = size;
  }
  
  void
  fonsSetColor(FONScontext* stash, unsigned int color)
  {
    fons__getState(stash)->color = color;
  }
  
  void
  fonsSetSpacing(FONScontext* stash, float spacing)
  {
    fons__getState(stash)->spacing = spacing;
  }
  
  void
  fonsSetBlur(FONScontext* stash, float blur)
  {
    fons__getState(stash)->blur = blur;
  }
  
  void
  fonsSetAlign(FONScontext* stash, int align)
  {
    fons__getState(stash)->align = align;
  }
  
  void
  fonsSetFont(FONScontext* stash, int font)
  {
    fons__getState(stash)->font = font;
  }
  
  void
  fonsPushState(FONScontext* stash)
  {
    if (stash->nstates >= FONS_MAX_STATES) {
      if (stash->handleError)
        stash->handleError(stash->errorUptr, FONS_STATES_OVERFLOW, 0);
      return;
    }
    if (stash->nstates > 0)
      memcpy(&stash->states[stash->nstates],
             &stash->states[stash->nstates - 1],
             sizeof(FONSstate));
    stash->nstates++;
  }
  
  void
  fonsPopState(FONScontext* stash)
  {
    if (stash->nstates <= 1) {
      if (stash->handleError)
        stash->handleError(stash->errorUptr, FONS_STATES_UNDERFLOW, 0);
      return;
    }
    stash->nstates--;
  }
  
  void
  fonsClearState(FONScontext* stash)
  {
    FONSstate* state = fons__getState(stash);
    state->size = 12.0f;
    state->color = 0xffffffff;
    state->font = 0;
    state->blur = 0;
    state->spacing = 0;
    state->align = FONS_ALIGN_LEFT | FONS_ALIGN_BASELINE;
  }
  
  static void
  fons__freeFont(FONSfont* font)
  {
    if (font == NULL)
      return;
    if (font->glyphs)
      free(font->glyphs);
    if (font->freeData && font->data)
      free(font->data);
    free(font);
  }
  
  static int
  fons__allocFont(FONScontext* stash)
  {
    FONSfont* font = NULL;
    if (stash->nfonts + 1 > stash->cfonts) {
      stash->cfonts = stash->cfonts == 0 ? 8 : stash->cfonts * 2;
      stash->fonts =
        (FONSfont**)realloc(stash->fonts, sizeof(FONSfont*) * stash->cfonts);
      if (stash->fonts == NULL)
        return -1;
    }
    font = (FONSfont*)malloc(sizeof(FONSfont));
    if (font == NULL)
      goto error;
    memset(font, 0, sizeof(FONSfont));
  
    font->glyphs = (FONSglyph*)malloc(sizeof(FONSglyph) * FONS_INIT_GLYPHS);
    if (font->glyphs == NULL)
      goto error;
    font->cglyphs = FONS_INIT_GLYPHS;
    font->nglyphs = 0;
  
    stash->fonts[stash->nfonts++] = font;
    return stash->nfonts - 1;
  
  error:
    fons__freeFont(font);
  
    return FONS_INVALID;
  }
  
  int
  fonsAddFont(FONScontext* stash, const char* name, const char* path)
  {
    FILE* fp = 0;
    int dataSize = 0;
    size_t readed;
    unsigned char* data = NULL;
  
    // Read in the font data.
    fp = fopen(path, "rb");
    if (fp == NULL)
      goto error;
    fseek(fp, 0, SEEK_END);
    dataSize = (int)ftell(fp);
    fseek(fp, 0, SEEK_SET);
    data = (unsigned char*)malloc(dataSize);
    if (data == NULL)
      goto error;
    readed = fread(data, 1, dataSize, fp);
    fclose(fp);
    fp = 0;
    if (readed != dataSize)
      goto error;
  
    return fonsAddFontMem(stash, name, data, dataSize, 1);
  
  error:
    if (data)
      free(data);
    if (fp)
      fclose(fp);
    return FONS_INVALID;
  }
  
  int
  fonsAddFontMem(FONScontext* stash,
                 const char* name,
                 unsigned char* data,
                 int dataSize,
                 int freeData)
  {
    int i, ascent, descent, fh, lineGap;
    FONSfont* font;
  
    int idx = fons__allocFont(stash);
    if (idx == FONS_INVALID)
      return FONS_INVALID;
  
    font = stash->fonts[idx];
  
    strncpy(font->name, name, sizeof(font->name));
    font->name[sizeof(font->name) - 1] = '\0';
  
    // Init hash lookup.
    for (i = 0; i < FONS_HASH_LUT_SIZE; ++i)
      font->lut[i] = -1;
  
    // Read in the font data.
    font->dataSize = dataSize;
    font->data = data;
    font->freeData = (unsigned char)freeData;
  
    // Init font
    stash->nscratch = 0;
    if (!fons__tt_loadFont(stash, &font->font, data, dataSize))
      goto error;
  
    // Store normalized line height. The real line height is got
    // by multiplying the lineh by font size.
    fons__tt_getFontVMetrics(&font->font, &ascent, &descent, &lineGap);
    fh = ascent - descent;
    font->ascender = (float)ascent / (float)fh;
    font->descender = (float)descent / (float)fh;
    font->lineh = (float)(fh + lineGap) / (float)fh;
  
    return idx;
  
  error:
    fons__freeFont(font);
    stash->nfonts--;
    return FONS_INVALID;
  }
  
  int
  fonsGetFontByName(FONScontext* s, const char* name)
  {
    int i;
    for (i = 0; i < s->nfonts; i++) {
      if (strcmp(s->fonts[i]->name, name) == 0)
        return i;
    }
    return FONS_INVALID;
  }
  
  static FONSglyph*
  fons__allocGlyph(FONSfont* font)
  {
    if (font->nglyphs + 1 > font->cglyphs) {
      font->cglyphs = font->cglyphs == 0 ? 8 : font->cglyphs * 2;
      font->glyphs =
        (FONSglyph*)realloc(font->glyphs, sizeof(FONSglyph) * font->cglyphs);
      if (font->glyphs == NULL)
        return NULL;
    }
    font->nglyphs++;
    return &font->glyphs[font->nglyphs - 1];
  }
  
  // Based on Exponential blur, Jani Huhtanen, 2006
  
  #define APREC 16
  #define ZPREC 7
  
  static void
  fons__blurCols(unsigned char* dst, int w, int h, int dstStride, int alpha)
  {
    int x, y;
    for (y = 0; y < h; y++) {
      int z = 0; // force zero border
      for (x = 1; x < w; x++) {
        z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC;
        dst[x] = (unsigned char)(z >> ZPREC);
      }
      dst[w - 1] = 0; // force zero border
      z = 0;
      for (x = w - 2; x >= 0; x--) {
        z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC;
        dst[x] = (unsigned char)(z >> ZPREC);
      }
      dst[0] = 0; // force zero border
      dst += dstStride;
    }
  }
  
  static void
  fons__blurRows(unsigned char* dst, int w, int h, int dstStride, int alpha)
  {
    int x, y;
    for (x = 0; x < w; x++) {
      int z = 0; // force zero border
      for (y = dstStride; y < h * dstStride; y += dstStride) {
        z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC;
        dst[y] = (unsigned char)(z >> ZPREC);
      }
      dst[(h - 1) * dstStride] = 0; // force zero border
      z = 0;
      for (y = (h - 2) * dstStride; y >= 0; y -= dstStride) {
        z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC;
        dst[y] = (unsigned char)(z >> ZPREC);
      }
      dst[0] = 0; // force zero border
      dst++;
    }
  }
  
  static void
  fons__blur(FONScontext* stash,
             unsigned char* dst,
             int w,
             int h,
             int dstStride,
             int blur)
  {
    int alpha;
    float sigma;
    (void)stash;
  
    if (blur < 1)
      return;
    // Calculate the alpha such that 90% of the kernel is within the radius.
    // (Kernel extends to infinity)
    sigma = (float)blur * 0.57735f; // 1 / sqrt(3)
    alpha = (int)((1 << APREC) * (1.0f - expf(-2.3f / (sigma + 1.0f))));
    fons__blurRows(dst, w, h, dstStride, alpha);
    fons__blurCols(dst, w, h, dstStride, alpha);
    fons__blurRows(dst, w, h, dstStride, alpha);
    fons__blurCols(dst, w, h, dstStride, alpha);
    //	fons__blurrows(dst, w, h, dstStride, alpha);
    //	fons__blurcols(dst, w, h, dstStride, alpha);
  }
  
  static FONSglyph*
  fons__getGlyph(FONScontext* stash,
                 FONSfont* font,
                 unsigned int codepoint,
                 short isize,
                 short iblur,
                 int bitmapOption)
  {
    int i, g, advance, lsb, x0, y0, x1, y1, gw, gh, gx, gy, x, y;
    float scale;
    FONSglyph* glyph = NULL;
    unsigned int h;
    float size = isize / 10.0f;
    int pad, added;
    unsigned char* bdst;
    unsigned char* dst;
    FONSfont* renderFont = font;
  
    if (isize < 2)
      return NULL;
    if (iblur > 20)
      iblur = 20;
    pad = iblur + 2;
  
    // Reset allocator.
    stash->nscratch = 0;
  
    // Find code point and size.
    h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE - 1);
    i = font->lut[h];
    while (i != -1) {
      if (font->glyphs[i].codepoint == codepoint &&
          font->glyphs[i].size == isize && font->glyphs[i].blur == iblur) {
        glyph = &font->glyphs[i];
        if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL ||
            (glyph->x0 >= 0 && glyph->y0 >= 0)) {
          return glyph;
        }
        // At this point, glyph exists but the bitmap data is not yet created.
        break;
      }
      i = font->glyphs[i].next;
    }
  
    // Create a new glyph or rasterize bitmap data for a cached glyph.
    g = fons__tt_getGlyphIndex(&font->font, codepoint);
    // Try to find the glyph in fallback fonts.
    if (g == 0) {
      for (i = 0; i < font->nfallbacks; ++i) {
        FONSfont* fallbackFont = stash->fonts[font->fallbacks[i]];
        int fallbackIndex =
          fons__tt_getGlyphIndex(&fallbackFont->font, codepoint);
        if (fallbackIndex != 0) {
          g = fallbackIndex;
          renderFont = fallbackFont;
          break;
        }
      }
      // It is possible that we did not find a fallback glyph.
      // In that case the glyph index 'g' is 0, and we'll proceed below and cache
      // empty glyph.
    }
    scale = fons__tt_getPixelHeightScale(&renderFont->font, size);
    fons__tt_buildGlyphBitmap(
      &renderFont->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1);
    gw = x1 - x0 + pad * 2;
    gh = y1 - y0 + pad * 2;
  
    // Determines the spot to draw glyph in the atlas.
    if (bitmapOption == FONS_GLYPH_BITMAP_REQUIRED) {
      // Find free spot for the rect in the atlas
      added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy);
      if (added == 0 && stash->handleError != NULL) {
        // Atlas is full, let the user to resize the atlas (or not), and try
        // again.
        stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0);
        added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy);
      }
      if (added == 0)
        return NULL;
    } else {
      // Negative coordinate indicates there is no bitmap data created.
      gx = -1;
      gy = -1;
    }
  
    // Init glyph.
    if (glyph == NULL) {
      glyph = fons__allocGlyph(font);
      glyph->codepoint = codepoint;
      glyph->size = isize;
      glyph->blur = iblur;
      glyph->next = 0;
  
      // Insert char to hash lookup.
      glyph->next = font->lut[h];
      font->lut[h] = font->nglyphs - 1;
    }
    glyph->index = g;
    glyph->x0 = (short)gx;
    glyph->y0 = (short)gy;
    glyph->x1 = (short)(glyph->x0 + gw);
    glyph->y1 = (short)(glyph->y0 + gh);
    glyph->xadv = (short)(scale * advance * 10.0f);
    glyph->xoff = (short)(x0 - pad);
    glyph->yoff = (short)(y0 - pad);
  
    if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL) {
      return glyph;
    }
  
    // Rasterize
    dst =
      &stash
         ->texData[(glyph->x0 + pad) + (glyph->y0 + pad) * stash->params.width];
    fons__tt_renderGlyphBitmap(&renderFont->font,
                               dst,
                               gw - pad * 2,
                               gh - pad * 2,
                               stash->params.width,
                               scale,
                               scale,
                               g);
  
    // Make sure there is one pixel empty border.
    dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width];
    for (y = 0; y < gh; y++) {
      dst[y * stash->params.width] = 0;
      dst[gw - 1 + y * stash->params.width] = 0;
    }
    for (x = 0; x < gw; x++) {
      dst[x] = 0;
      dst[x + (gh - 1) * stash->params.width] = 0;
    }
  
    // Debug code to color the glyph background
    /*	unsigned char* fdst = &stash->texData[glyph->x0 + glyph->y0 *
       stash->params.width]; for (y = 0; y < gh; y++) { for (x = 0; x < gw; x++) {
                            int a = (int)fdst[x+y*stash->params.width] + 20;
                            if (a > 255) a = 255;
                            fdst[x+y*stash->params.width] = a;
                    }
            }*/
  
    // Blur
    if (iblur > 0) {
      stash->nscratch = 0;
      bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width];
      fons__blur(stash, bdst, gw, gh, stash->params.width, iblur);
    }
  
    stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0);
    stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], glyph->y0);
    stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], glyph->x1);
    stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], glyph->y1);
  
    return glyph;
  }
  
  static void
  fons__getQuad(FONScontext* stash,
                FONSfont* font,
                int prevGlyphIndex,
                FONSglyph* glyph,
                float scale,
                float spacing,
                float* x,
                float* y,
                FONSquad* q)
  {
    float rx, ry, xoff, yoff, x0, y0, x1, y1;
  
    if (prevGlyphIndex != -1) {
      float adv =
        fons__tt_getGlyphKernAdvance(&font->font, prevGlyphIndex, glyph->index) *
        scale;
      *x += (int)(adv + spacing + 0.5f);
    }
  
    // Each glyph has 2px border to allow good interpolation,
    // one pixel to prevent leaking, and one to allow good interpolation for
    // rendering. Inset the texture region by one pixel for correct interpolation.
    xoff = (short)(glyph->xoff + 1);
    yoff = (short)(glyph->yoff + 1);
    x0 = (float)(glyph->x0 + 1);
    y0 = (float)(glyph->y0 + 1);
    x1 = (float)(glyph->x1 - 1);
    y1 = (float)(glyph->y1 - 1);
  
    if (stash->params.flags & FONS_ZERO_TOPLEFT) {
      rx = (float)(int)(*x + xoff);
      ry = (float)(int)(*y + yoff);
  
      q->x0 = rx;
      q->y0 = ry;
      q->x1 = rx + x1 - x0;
      q->y1 = ry + y1 - y0;
  
      q->s0 = x0 * stash->itw;
      q->t0 = y0 * stash->ith;
      q->s1 = x1 * stash->itw;
      q->t1 = y1 * stash->ith;
    } else {
      rx = (float)(int)(*x + xoff);
      ry = (float)(int)(*y - yoff);
  
      q->x0 = rx;
      q->y0 = ry;
      q->x1 = rx + x1 - x0;
      q->y1 = ry - y1 + y0;
  
      q->s0 = x0 * stash->itw;
      q->t0 = y0 * stash->ith;
      q->s1 = x1 * stash->itw;
      q->t1 = y1 * stash->ith;
    }
  
    *x += (int)(glyph->xadv / 10.0f + 0.5f);
  }
  
  static void
  fons__flush(FONScontext* stash)
  {
    // Flush texture
    if (stash->dirtyRect[0] < stash->dirtyRect[2] &&
        stash->dirtyRect[1] < stash->dirtyRect[3]) {
      if (stash->params.renderUpdate != NULL)
        stash->params.renderUpdate(
          stash->params.userPtr, stash->dirtyRect, stash->texData);
      // Reset dirty rect
      stash->dirtyRect[0] = stash->params.width;
      stash->dirtyRect[1] = stash->params.height;
      stash->dirtyRect[2] = 0;
      stash->dirtyRect[3] = 0;
    }
  
    // Flush triangles
    if (stash->nverts > 0) {
      if (stash->params.renderDraw != NULL)
        stash->params.renderDraw(stash->params.userPtr,
                                 stash->verts,
                                 stash->tcoords,
                                 stash->colors,
                                 stash->nverts);
      stash->nverts = 0;
    }
  }
  
  static __inline void
  fons__vertex(FONScontext* stash,
               float x,
               float y,
               float s,
               float t,
               unsigned int c)
  {
    stash->verts[stash->nverts * 2 + 0] = x;
    stash->verts[stash->nverts * 2 + 1] = y;
    stash->tcoords[stash->nverts * 2 + 0] = s;
    stash->tcoords[stash->nverts * 2 + 1] = t;
    stash->colors[stash->nverts] = c;
    stash->nverts++;
  }
  
  static float
  fons__getVertAlign(FONScontext* stash, FONSfont* font, int align, short isize)
  {
    if (stash->params.flags & FONS_ZERO_TOPLEFT) {
      if (align & FONS_ALIGN_TOP) {
        return font->ascender * (float)isize / 10.0f;
      } else if (align & FONS_ALIGN_MIDDLE) {
        return (font->ascender + font->descender) / 2.0f * (float)isize / 10.0f;
      } else if (align & FONS_ALIGN_BASELINE) {
        return 0.0f;
      } else if (align & FONS_ALIGN_BOTTOM) {
        return font->descender * (float)isize / 10.0f;
      }
    } else {
      if (align & FONS_ALIGN_TOP) {
        return -font->ascender * (float)isize / 10.0f;
      } else if (align & FONS_ALIGN_MIDDLE) {
        return -(font->ascender + font->descender) / 2.0f * (float)isize / 10.0f;
      } else if (align & FONS_ALIGN_BASELINE) {
        return 0.0f;
      } else if (align & FONS_ALIGN_BOTTOM) {
        return -font->descender * (float)isize / 10.0f;
      }
    }
    return 0.0;
  }
  
  float
  fonsDrawText(FONScontext* stash,
               float x,
               float y,
               const char* str,
               const char* end)
  {
    FONSstate* state = fons__getState(stash);
    unsigned int codepoint;
    unsigned int utf8state = 0;
    FONSglyph* glyph = NULL;
    FONSquad q;
    int prevGlyphIndex = -1;
    short isize = (short)(state->size * 10.0f);
    short iblur = (short)state->blur;
    float scale;
    FONSfont* font;
    float width;
  
    if (stash == NULL)
      return x;
    if (state->font < 0 || state->font >= stash->nfonts)
      return x;
    font = stash->fonts[state->font];
    if (font->data == NULL)
      return x;
  
    scale = fons__tt_getPixelHeightScale(&font->font, (float)isize / 10.0f);
  
    if (end == NULL)
      end = str + strlen(str);
  
    // Align horizontally
    if (state->align & FONS_ALIGN_LEFT) {
      // empty
    } else if (state->align & FONS_ALIGN_RIGHT) {
      width = fonsTextBounds(stash, x, y, str, end, NULL);
      x -= width;
    } else if (state->align & FONS_ALIGN_CENTER) {
      width = fonsTextBounds(stash, x, y, str, end, NULL);
      x -= width * 0.5f;
    }
    // Align vertically.
    y += fons__getVertAlign(stash, font, state->align, isize);
  
    for (; str != end; ++str) {
      if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str))
        continue;
      glyph = fons__getGlyph(
        stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_REQUIRED);
      if (glyph != NULL) {
        fons__getQuad(
          stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q);
  
        if (stash->nverts + 6 > FONS_VERTEX_COUNT)
          fons__flush(stash);
  
        fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color);
        fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color);
        fons__vertex(stash, q.x1, q.y0, q.s1, q.t0, state->color);
  
        fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color);
        fons__vertex(stash, q.x0, q.y1, q.s0, q.t1, state->color);
        fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color);
      }
      prevGlyphIndex = glyph != NULL ? glyph->index : -1;
    }
    fons__flush(stash);
  
    return x;
  }
  
  int
  fonsTextIterInit(FONScontext* stash,
                   FONStextIter* iter,
                   float x,
                   float y,
                   const char* str,
                   const char* end,
                   int bitmapOption)
  {
    FONSstate* state = fons__getState(stash);
    float width;
  
    memset(iter, 0, sizeof(*iter));
  
    if (stash == NULL)
      return 0;
    if (state->font < 0 || state->font >= stash->nfonts)
      return 0;
    iter->font = stash->fonts[state->font];
    if (iter->font->data == NULL)
      return 0;
  
    iter->isize = (short)(state->size * 10.0f);
    iter->iblur = (short)state->blur;
    iter->scale =
      fons__tt_getPixelHeightScale(&iter->font->font, (float)iter->isize / 10.0f);
  
    // Align horizontally
    if (state->align & FONS_ALIGN_LEFT) {
      // empty
    } else if (state->align & FONS_ALIGN_RIGHT) {
      width = fonsTextBounds(stash, x, y, str, end, NULL);
      x -= width;
    } else if (state->align & FONS_ALIGN_CENTER) {
      width = fonsTextBounds(stash, x, y, str, end, NULL);
      x -= width * 0.5f;
    }
    // Align vertically.
    y += fons__getVertAlign(stash, iter->font, state->align, iter->isize);
  
    if (end == NULL)
      end = str + strlen(str);
  
    iter->x = iter->nextx = x;
    iter->y = iter->nexty = y;
    iter->spacing = state->spacing;
    iter->str = str;
    iter->next = str;
    iter->end = end;
    iter->codepoint = 0;
    iter->prevGlyphIndex = -1;
    iter->bitmapOption = bitmapOption;
  
    return 1;
  }
  
  int
  fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad)
  {
    FONSglyph* glyph = NULL;
    const char* str = iter->next;
    iter->str = iter->next;
  
    if (str == iter->end)
      return 0;
  
    for (; str != iter->end; str++) {
      if (fons__decutf8(
            &iter->utf8state, &iter->codepoint, *(const unsigned char*)str))
        continue;
      str++;
      // Get glyph and quad
      iter->x = iter->nextx;
      iter->y = iter->nexty;
      glyph = fons__getGlyph(stash,
                             iter->font,
                             iter->codepoint,
                             iter->isize,
                             iter->iblur,
                             iter->bitmapOption);
      // If the iterator was initialized with FONS_GLYPH_BITMAP_OPTIONAL, then the
      // UV coordinates of the quad will be invalid.
      if (glyph != NULL)
        fons__getQuad(stash,
                      iter->font,
                      iter->prevGlyphIndex,
                      glyph,
                      iter->scale,
                      iter->spacing,
                      &iter->nextx,
                      &iter->nexty,
                      quad);
      iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1;
      break;
    }
    iter->next = str;
  
    return 1;
  }
  
  void
  fonsDrawDebug(FONScontext* stash, float x, float y)
  {
    int i;
    int w = stash->params.width;
    int h = stash->params.height;
    float u = w == 0 ? 0 : (1.0f / w);
    float v = h == 0 ? 0 : (1.0f / h);
  
    if (stash->nverts + 6 + 6 > FONS_VERTEX_COUNT)
      fons__flush(stash);
  
    // Draw background
    fons__vertex(stash, x + 0, y + 0, u, v, 0x0fffffff);
    fons__vertex(stash, x + w, y + h, u, v, 0x0fffffff);
    fons__vertex(stash, x + w, y + 0, u, v, 0x0fffffff);
  
    fons__vertex(stash, x + 0, y + 0, u, v, 0x0fffffff);
    fons__vertex(stash, x + 0, y + h, u, v, 0x0fffffff);
    fons__vertex(stash, x + w, y + h, u, v, 0x0fffffff);
  
    // Draw texture
    fons__vertex(stash, x + 0, y + 0, 0, 0, 0xffffffff);
    fons__vertex(stash, x + w, y + h, 1, 1, 0xffffffff);
    fons__vertex(stash, x + w, y + 0, 1, 0, 0xffffffff);
  
    fons__vertex(stash, x + 0, y + 0, 0, 0, 0xffffffff);
    fons__vertex(stash, x + 0, y + h, 0, 1, 0xffffffff);
    fons__vertex(stash, x + w, y + h, 1, 1, 0xffffffff);
  
    // Drawbug draw atlas
    for (i = 0; i < stash->atlas->nnodes; i++) {
      FONSatlasNode* n = &stash->atlas->nodes[i];
  
      if (stash->nverts + 6 > FONS_VERTEX_COUNT)
        fons__flush(stash);
  
      fons__vertex(stash, x + n->x + 0, y + n->y + 0, u, v, 0xc00000ff);
      fons__vertex(stash, x + n->x + n->width, y + n->y + 1, u, v, 0xc00000ff);
      fons__vertex(stash, x + n->x + n->width, y + n->y + 0, u, v, 0xc00000ff);
  
      fons__vertex(stash, x + n->x + 0, y + n->y + 0, u, v, 0xc00000ff);
      fons__vertex(stash, x + n->x + 0, y + n->y + 1, u, v, 0xc00000ff);
      fons__vertex(stash, x + n->x + n->width, y + n->y + 1, u, v, 0xc00000ff);
    }
  
    fons__flush(stash);
  }
  
  float
  fonsTextBounds(FONScontext* stash,
                 float x,
                 float y,
                 const char* str,
                 const char* end,
                 float* bounds)
  {
    FONSstate* state = fons__getState(stash);
    unsigned int codepoint;
    unsigned int utf8state = 0;
    FONSquad q;
    FONSglyph* glyph = NULL;
    int prevGlyphIndex = -1;
    short isize = (short)(state->size * 10.0f);
    short iblur = (short)state->blur;
    float scale;
    FONSfont* font;
    float startx, advance;
    float minx, miny, maxx, maxy;
  
    if (stash == NULL)
      return 0;
    if (state->font < 0 || state->font >= stash->nfonts)
      return 0;
    font = stash->fonts[state->font];
    if (font->data == NULL)
      return 0;
  
    scale = fons__tt_getPixelHeightScale(&font->font, (float)isize / 10.0f);
  
    // Align vertically.
    y += fons__getVertAlign(stash, font, state->align, isize);
  
    minx = maxx = x;
    miny = maxy = y;
    startx = x;
  
    if (end == NULL)
      end = str + strlen(str);
  
    for (; str != end; ++str) {
      if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str))
        continue;
      glyph = fons__getGlyph(
        stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_OPTIONAL);
      if (glyph != NULL) {
        fons__getQuad(
          stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q);
        if (q.x0 < minx)
          minx = q.x0;
        if (q.x1 > maxx)
          maxx = q.x1;
        if (stash->params.flags & FONS_ZERO_TOPLEFT) {
          if (q.y0 < miny)
            miny = q.y0;
          if (q.y1 > maxy)
            maxy = q.y1;
        } else {
          if (q.y1 < miny)
            miny = q.y1;
          if (q.y0 > maxy)
            maxy = q.y0;
        }
      }
      prevGlyphIndex = glyph != NULL ? glyph->index : -1;
    }
  
    advance = x - startx;
  
    // Align horizontally
    if (state->align & FONS_ALIGN_LEFT) {
      // empty
    } else if (state->align & FONS_ALIGN_RIGHT) {
      minx -= advance;
      maxx -= advance;
    } else if (state->align & FONS_ALIGN_CENTER) {
      minx -= advance * 0.5f;
      maxx -= advance * 0.5f;
    }
  
    if (bounds) {
      bounds[0] = minx;
      bounds[1] = miny;
      bounds[2] = maxx;
      bounds[3] = maxy;
    }
  
    return advance;
  }
  
  void
  fonsVertMetrics(FONScontext* stash,
                  float* ascender,
                  float* descender,
                  float* lineh)
  {
    FONSfont* font;
    FONSstate* state = fons__getState(stash);
    short isize;
  
    if (stash == NULL)
      return;
    if (state->font < 0 || state->font >= stash->nfonts)
      return;
    font = stash->fonts[state->font];
    isize = (short)(state->size * 10.0f);
    if (font->data == NULL)
      return;
  
    if (ascender)
      *ascender = font->ascender * isize / 10.0f;
    if (descender)
      *descender = font->descender * isize / 10.0f;
    if (lineh)
      *lineh = font->lineh * isize / 10.0f;
  }
  
  void
  fonsLineBounds(FONScontext* stash, float y, float* miny, float* maxy)
  {
    FONSfont* font;
    FONSstate* state = fons__getState(stash);
    short isize;
  
    if (stash == NULL)
      return;
    if (state->font < 0 || state->font >= stash->nfonts)
      return;
    font = stash->fonts[state->font];
    isize = (short)(state->size * 10.0f);
    if (font->data == NULL)
      return;
  
    y += fons__getVertAlign(stash, font, state->align, isize);
  
    if (stash->params.flags & FONS_ZERO_TOPLEFT) {
      *miny = y - font->ascender * (float)isize / 10.0f;
      *maxy = *miny + font->lineh * isize / 10.0f;
    } else {
      *maxy = y + font->descender * (float)isize / 10.0f;
      *miny = *maxy - font->lineh * isize / 10.0f;
    }
  }
  
  const unsigned char*
  fonsGetTextureData(FONScontext* stash, int* width, int* height)
  {
    if (width != NULL)
      *width = stash->params.width;
    if (height != NULL)
      *height = stash->params.height;
    return stash->texData;
  }
  
  int
  fonsValidateTexture(FONScontext* stash, int* dirty)
  {
    if (stash->dirtyRect[0] < stash->dirtyRect[2] &&
        stash->dirtyRect[1] < stash->dirtyRect[3]) {
      dirty[0] = stash->dirtyRect[0];
      dirty[1] = stash->dirtyRect[1];
      dirty[2] = stash->dirtyRect[2];
      dirty[3] = stash->dirtyRect[3];
      // Reset dirty rect
      stash->dirtyRect[0] = stash->params.width;
      stash->dirtyRect[1] = stash->params.height;
      stash->dirtyRect[2] = 0;
      stash->dirtyRect[3] = 0;
      return 1;
    }
    return 0;
  }
  
  void
  fonsDeleteInternal(FONScontext* stash)
  {
    int i;
    if (stash == NULL)
      return;
  
    if (stash->params.renderDelete)
      stash->params.renderDelete(stash->params.userPtr);
  
    for (i = 0; i < stash->nfonts; ++i)
      fons__freeFont(stash->fonts[i]);
  
    if (stash->atlas)
      fons__deleteAtlas(stash->atlas);
    if (stash->fonts)
      free(stash->fonts);
    if (stash->texData)
      free(stash->texData);
    if (stash->scratch)
      free(stash->scratch);
    free(stash);
  }
  
  void
  fonsSetErrorCallback(FONScontext* stash,
                       void (*callback)(void* uptr, int error, int val),
                       void* uptr)
  {
    if (stash == NULL)
      return;
    stash->handleError = callback;
    stash->errorUptr = uptr;
  }
  
  void
  fonsGetAtlasSize(FONScontext* stash, int* width, int* height)
  {
    if (stash == NULL)
      return;
    *width = stash->params.width;
    *height = stash->params.height;
  }
  
  int
  fonsExpandAtlas(FONScontext* stash, int width, int height)
  {
    int i, maxy = 0;
    unsigned char* data = NULL;
    if (stash == NULL)
      return 0;
  
    width = fons__maxi(width, stash->params.width);
    height = fons__maxi(height, stash->params.height);
  
    if (width == stash->params.width && height == stash->params.height)
      return 1;
  
    // Flush pending glyphs.
    fons__flush(stash);
  
    // Create new texture
    if (stash->params.renderResize != NULL) {
      if (stash->params.renderResize(stash->params.userPtr, width, height) == 0)
        return 0;
    }
    // Copy old texture data over.
    data = (unsigned char*)malloc(width * height);
    if (data == NULL)
      return 0;
    for (i = 0; i < stash->params.height; i++) {
      unsigned char* dst = &data[i * width];
      unsigned char* src = &stash->texData[i * stash->params.width];
      memcpy(dst, src, stash->params.width);
      if (width > stash->params.width)
        memset(dst + stash->params.width, 0, width - stash->params.width);
    }
    if (height > stash->params.height)
      memset(&data[stash->params.height * width],
             0,
             (height - stash->params.height) * width);
  
    free(stash->texData);
    stash->texData = data;
  
    // Increase atlas size
    fons__atlasExpand(stash->atlas, width, height);
  
    // Add existing data as dirty.
    for (i = 0; i < stash->atlas->nnodes; i++)
      maxy = fons__maxi(maxy, stash->atlas->nodes[i].y);
    stash->dirtyRect[0] = 0;
    stash->dirtyRect[1] = 0;
    stash->dirtyRect[2] = stash->params.width;
    stash->dirtyRect[3] = maxy;
  
    stash->params.width = width;
    stash->params.height = height;
    stash->itw = 1.0f / stash->params.width;
    stash->ith = 1.0f / stash->params.height;
  
    return 1;
  }
  
  int
  fonsResetAtlas(FONScontext* stash, int width, int height)
  {
    int i, j;
    if (stash == NULL)
      return 0;
  
    // Flush pending glyphs.
    fons__flush(stash);
  
    // Create new texture
    if (stash->params.renderResize != NULL) {
      if (stash->params.renderResize(stash->params.userPtr, width, height) == 0)
        return 0;
    }
  
    // Reset atlas
    fons__atlasReset(stash->atlas, width, height);
  
    // Clear texture data.
    stash->texData = (unsigned char*)realloc(stash->texData, width * height);
    if (stash->texData == NULL)
      return 0;
    memset(stash->texData, 0, width * height);
  
    // Reset dirty rect
    stash->dirtyRect[0] = width;
    stash->dirtyRect[1] = height;
    stash->dirtyRect[2] = 0;
    stash->dirtyRect[3] = 0;
  
    // Reset cached glyphs
    for (i = 0; i < stash->nfonts; i++) {
      FONSfont* font = stash->fonts[i];
      font->nglyphs = 0;
      for (j = 0; j < FONS_HASH_LUT_SIZE; j++)
        font->lut[j] = -1;
    }
  
    stash->params.width = width;
    stash->params.height = height;
    stash->itw = 1.0f / stash->params.width;
    stash->ith = 1.0f / stash->params.height;
  
    // Add white rect at 0,0 for debug drawing.
    fons__addWhiteRect(stash, 2, 2);
  
    return 1;
  }
  
  #endif
  