text: /* Freetype GL - A C OpenGL Freetype engine
   *
   * Distributed under the OSI-approved BSD 2-Clause License.  See accompanying
   * file `LICENSE` for more details.
   */
  #include <ft2build.h>
  #include FT_FREETYPE_H
  #include FT_STROKER_H
  // #include FT_ADVANCES_H
  #include FT_LCD_FILTER_H
  #include <stdint.h>
  #include <stdlib.h>
  #include <stdio.h>
  #include <assert.h>
  #include <math.h>
  #include "distance-field.h"
  #include "texture-font.h"
  #include "platform.h"
  #include "utf8-utils.h"
  
  #define HRES  64
  #define HRESf 64.f
  #define DPI   72
  
  #undef __FTERRORS_H__
  #define FT_ERRORDEF( e, v, s )  { e, s },
  #define FT_ERROR_START_LIST     {
  #define FT_ERROR_END_LIST       { 0, 0 } };
  const struct {
      int          code;
      const char*  message;
  } FT_Errors[] =
  #include FT_ERRORS_H
  
  // ------------------------------------------------- texture_font_load_face ---
  static int
  texture_font_load_face(texture_font_t *self, float size,
          FT_Library *library, FT_Face *face)
  {
      FT_Error error;
      FT_Matrix matrix = {
          (int)((1.0/HRES) * 0x10000L),
          (int)((0.0)      * 0x10000L),
          (int)((0.0)      * 0x10000L),
          (int)((1.0)      * 0x10000L)};
  
      assert(library);
      assert(size);
  
      /* Initialize library */
      error = FT_Init_FreeType(library);
      if(error) {
          fprintf(stderr, "FT_Error (0x%02x) : %s\n",
                  FT_Errors[error].code, FT_Errors[error].message);
          goto cleanup;
      }
  
      /* Load face */
      switch (self->location) {
      case TEXTURE_FONT_FILE:
          error = FT_New_Face(*library, self->filename, 0, face);
          break;
  
      case TEXTURE_FONT_MEMORY:
          error = FT_New_Memory_Face(*library,
              self->memory.base, (FT_Long)self->memory.size, 0, face);
          break;
      }
  
      if(error) {
          fprintf(stderr, "FT_Error (line %d, code 0x%02x) : %s\n",
                  __LINE__, FT_Errors[error].code, FT_Errors[error].message);
          goto cleanup_library;
      }
  
      /* Select charmap */
      error = FT_Select_Charmap(*face, FT_ENCODING_UNICODE);
      if(error) {
          fprintf(stderr, "FT_Error (line %d, code 0x%02x) : %s\n",
                  __LINE__, FT_Errors[error].code, FT_Errors[error].message);
          goto cleanup_face;
      }
  
      /* Set char size */
      error = FT_Set_Char_Size(*face, (int)(size * HRES), 0, DPI * HRES, DPI);
  
      if(error) {
          fprintf(stderr, "FT_Error (line %d, code 0x%02x) : %s\n",
                  __LINE__, FT_Errors[error].code, FT_Errors[error].message);
          goto cleanup_face;
      }
  
      /* Set transform matrix */
      FT_Set_Transform(*face, &matrix, NULL);
  
      return 1;
  
  cleanup_face:
      FT_Done_Face( *face );
  cleanup_library:
      FT_Done_FreeType( *library );
  cleanup:
      return 0;
  }
  
  // ------------------------------------------------------ texture_glyph_new ---
  texture_glyph_t *
  texture_glyph_new(void)
  {
      texture_glyph_t *self = (texture_glyph_t *) malloc( sizeof(texture_glyph_t) );
      if(self == NULL) {
          fprintf( stderr,
                  "line %d: No more memory for allocating data\n", __LINE__);
          return NULL;
      }
  
      self->codepoint  = -1;
      self->width     = 0;
      self->height    = 0;
      self->rendermode = RENDER_NORMAL;
      self->outline_thickness = 0.0;
      self->offset_x  = 0;
      self->offset_y  = 0;
      self->advance_x = 0.0;
      self->advance_y = 0.0;
      self->s0        = 0.0;
      self->t0        = 0.0;
      self->s1        = 0.0;
      self->t1        = 0.0;
      self->kerning   = vector_new( sizeof(kerning_t) );
      return self;
  }
  
  
  // --------------------------------------------------- texture_glyph_delete ---
  void
  texture_glyph_delete( texture_glyph_t *self )
  {
      assert( self );
      vector_delete( self->kerning );
      free( self );
  }
  
  // ---------------------------------------------- texture_glyph_get_kerning ---
  float
  texture_glyph_get_kerning( const texture_glyph_t * self,
                             const char * codepoint )
  {
      size_t i;
      uint32_t ucodepoint = utf8_to_utf32( codepoint );
  
      assert( self );
      for( i=0; i<vector_size(self->kerning); ++i )
      {
          kerning_t * kerning = (kerning_t *) vector_get( self->kerning, i );
          if( kerning->codepoint == ucodepoint )
          {
              return kerning->kerning;
          }
      }
      return 0;
  }
  
  
  // ------------------------------------------ texture_font_generate_kerning ---
  void
  texture_font_generate_kerning( texture_font_t *self,
                                 FT_Library *library, FT_Face *face )
  {
      size_t i, j;
      FT_UInt glyph_index, prev_index;
      texture_glyph_t *glyph, *prev_glyph;
      FT_Vector kerning;
  
      assert( self );
  
      /* For each glyph couple combination, check if kerning is necessary */
      /* Starts at index 1 since 0 is for the special backgroudn glyph */
      for( i=1; i<self->glyphs->size; ++i )
      {
          glyph = *(texture_glyph_t **) vector_get( self->glyphs, i );
          glyph_index = FT_Get_Char_Index( *face, glyph->codepoint );
          vector_clear( glyph->kerning );
  
          for( j=1; j<self->glyphs->size; ++j )
          {
              prev_glyph = *(texture_glyph_t **) vector_get( self->glyphs, j );
              prev_index = FT_Get_Char_Index( *face, prev_glyph->codepoint );
              FT_Get_Kerning( *face, prev_index, glyph_index, FT_KERNING_UNFITTED, &kerning );
              // printf("%c(%d)-%c(%d): %ld\n",
              //       prev_glyph->codepoint, prev_glyph->codepoint,
              //       glyph_index, glyph_index, kerning.x);
              if( kerning.x )
              {
                  kerning_t k = {prev_glyph->codepoint, kerning.x / (float)(HRESf*HRESf)};
                  vector_push_back( glyph->kerning, &k );
              }
          }
      }
  }
  
  // ------------------------------------------------------ texture_font_init ---
  static int
  texture_font_init(texture_font_t *self)
  {
      FT_Library library;
      FT_Face face;
      FT_Size_Metrics metrics;
  
      assert(self->atlas);
      assert(self->size > 0);
      assert((self->location == TEXTURE_FONT_FILE && self->filename)
          || (self->location == TEXTURE_FONT_MEMORY
              && self->memory.base && self->memory.size));
  
      self->glyphs = vector_new(sizeof(texture_glyph_t *));
      self->height = 0;
      self->ascender = 0;
      self->descender = 0;
      self->rendermode = RENDER_NORMAL;
      self->outline_thickness = 0.0;
      self->hinting = 1;
      self->kerning = 1;
      self->filtering = 1;
  
      // FT_LCD_FILTER_LIGHT   is (0x00, 0x55, 0x56, 0x55, 0x00)
      // FT_LCD_FILTER_DEFAULT is (0x10, 0x40, 0x70, 0x40, 0x10)
      self->lcd_weights[0] = 0x10;
      self->lcd_weights[1] = 0x40;
      self->lcd_weights[2] = 0x70;
      self->lcd_weights[3] = 0x40;
      self->lcd_weights[4] = 0x10;
  
      if (!texture_font_load_face(self, self->size * 100.f, &library, &face))
          return -1;
  
      self->underline_position = face->underline_position / (float)(HRESf*HRESf) * self->size;
      self->underline_position = roundf( self->underline_position );
      if( self->underline_position > -2 )
      {
          self->underline_position = -2.0;
      }
  
      self->underline_thickness = face->underline_thickness / (float)(HRESf*HRESf) * self->size;
      self->underline_thickness = roundf( self->underline_thickness );
      if( self->underline_thickness < 1 )
      {
          self->underline_thickness = 1.0;
      }
  
      metrics = face->size->metrics;
      self->ascender = (metrics.ascender >> 6) / 100.0;
      self->descender = (metrics.descender >> 6) / 100.0;
      self->height = (metrics.height >> 6) / 100.0;
      self->linegap = self->height - self->ascender + self->descender;
      FT_Done_Face( face );
      FT_Done_FreeType( library );
  
      /* NULL is a special glyph */
      texture_font_get_glyph( self, NULL );
  
      return 0;
  }
  
  // --------------------------------------------- texture_font_new_from_file ---
  texture_font_t *
  texture_font_new_from_file(texture_atlas_t *atlas, const float pt_size,
          const char *filename)
  {
      texture_font_t *self;
  
      assert(filename);
  
      self = calloc(1, sizeof(*self));
      if (!self) {
          fprintf(stderr,
                  "line %d: No more memory for allocating data\n", __LINE__);
          return NULL;
      }
  
      self->atlas = atlas;
      self->size  = pt_size;
  
      self->location = TEXTURE_FONT_FILE;
      self->filename = strdup(filename);
  
      if (texture_font_init(self)) {
          texture_font_delete(self);
          return NULL;
      }
  
      return self;
  }
  
  // ------------------------------------------- texture_font_new_from_memory ---
  texture_font_t *
  texture_font_new_from_memory(texture_atlas_t *atlas, float pt_size,
          const void *memory_base, size_t memory_size)
  {
      texture_font_t *self;
  
      assert(memory_base);
      assert(memory_size);
  
      self = calloc(1, sizeof(*self));
      if (!self) {
          fprintf(stderr,
                  "line %d: No more memory for allocating data\n", __LINE__);
          return NULL;
      }
  
      self->atlas = atlas;
      self->size  = pt_size;
  
      self->location = TEXTURE_FONT_MEMORY;
      self->memory.base = memory_base;
      self->memory.size = memory_size;
  
      if (texture_font_init(self)) {
          texture_font_delete(self);
          return NULL;
      }
  
      return self;
  }
  
  // ---------------------------------------------------- texture_font_delete ---
  void
  texture_font_delete( texture_font_t *self )
  {
      size_t i;
      texture_glyph_t *glyph;
  
      assert( self );
  
      if(self->location == TEXTURE_FONT_FILE && self->filename)
          free( self->filename );
  
      for( i=0; i<vector_size( self->glyphs ); ++i)
      {
          glyph = *(texture_glyph_t **) vector_get( self->glyphs, i );
          texture_glyph_delete( glyph);
      }
  
      vector_delete( self->glyphs );
      free( self );
  }
  
  texture_glyph_t *
  texture_font_find_glyph( texture_font_t * self,
                           const char * codepoint )
  {
      size_t i;
      texture_glyph_t *glyph;
      uint32_t ucodepoint = utf8_to_utf32( codepoint );
  
      for( i = 0; i < self->glyphs->size; ++i )
      {
          glyph = *(texture_glyph_t **) vector_get( self->glyphs, i );
          // If codepoint is -1, we don't care about outline type or thickness
          if( (glyph->codepoint == ucodepoint) &&
              ((ucodepoint == -1) ||
               ((glyph->rendermode == self->rendermode) &&
                (glyph->outline_thickness == self->outline_thickness)) ))
          {
              return glyph;
          }
      }
  
      return NULL;
  }
  
  // ------------------------------------------------ texture_font_load_glyph ---
  int
  texture_font_load_glyph( texture_font_t * self,
                           const char * codepoint )
  {
      size_t i, x, y;
  
      FT_Library library;
      FT_Error error;
      FT_Face face;
      FT_Glyph ft_glyph;
      FT_GlyphSlot slot;
      FT_Bitmap ft_bitmap;
  
      FT_UInt glyph_index;
      texture_glyph_t *glyph;
      FT_Int32 flags = 0;
      int ft_glyph_top = 0;
      int ft_glyph_left = 0;
  
      ivec4 region;
      size_t missed = 0;
  
  
      if (!texture_font_load_face(self, self->size, &library, &face))
          return 0;
  
      /* Check if codepoint has been already loaded */
      if (texture_font_find_glyph(self, codepoint)) {
          FT_Done_Face(face);
          FT_Done_FreeType(library);
          return 1;
      }
  
      /* codepoint NULL is special : it is used for line drawing (overline,
       * underline, strikethrough) and background.
       */
      if( !codepoint )
      {
          ivec4 region = texture_atlas_get_region( self->atlas, 5, 5 );
          texture_glyph_t * glyph = texture_glyph_new( );
          static unsigned char data[4*4*3] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                                              -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                                              -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
                                              -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
          if ( region.x < 0 )
          {
              fprintf( stderr, "Texture atlas is full (line %d)\n",  __LINE__ );
              FT_Done_Face( face );
              FT_Done_FreeType( library );
              return 0;
          }
          texture_atlas_set_region( self->atlas, region.x, region.y, 4, 4, data, 0 );
          glyph->codepoint = -1;
          glyph->s0 = (region.x+2)/(float)self->atlas->width;
          glyph->t0 = (region.y+2)/(float)self->atlas->height;
          glyph->s1 = (region.x+3)/(float)self->atlas->width;
          glyph->t1 = (region.y+3)/(float)self->atlas->height;
          vector_push_back( self->glyphs, &glyph );
  
          FT_Done_Face(face);
          FT_Done_FreeType(library);
          return 1;
      }
  
      flags = 0;
      ft_glyph_top = 0;
      ft_glyph_left = 0;
      glyph_index = FT_Get_Char_Index( face, (FT_ULong)utf8_to_utf32( codepoint ) );
      // WARNING: We use texture-atlas depth to guess if user wants
      //          LCD subpixel rendering
  
      if( self->rendermode != RENDER_NORMAL && self->rendermode != RENDER_SIGNED_DISTANCE_FIELD )
      {
          flags |= FT_LOAD_NO_BITMAP;
      }
      else
      {
          flags |= FT_LOAD_RENDER;
      }
  
      if( !self->hinting )
      {
          flags |= FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT;
      }
      else
      {
          flags |= FT_LOAD_FORCE_AUTOHINT;
      }
  
      if( self->atlas->depth == 3 )
      {
          FT_Library_SetLcdFilter( library, FT_LCD_FILTER_LIGHT );
          flags |= FT_LOAD_TARGET_LCD;
  
          if( self->filtering )
          {
              FT_Library_SetLcdFilterWeights( library, self->lcd_weights );
          }
      }
  
      error = FT_Load_Glyph( face, glyph_index, flags );
      if( error )
      {
          fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n",
                   __LINE__, FT_Errors[error].code, FT_Errors[error].message );
          FT_Done_Face( face );
          FT_Done_FreeType( library );
          return 0;
      }
  
      if( self->rendermode == RENDER_NORMAL || self->rendermode == RENDER_SIGNED_DISTANCE_FIELD )
      {
          slot            = face->glyph;
          ft_bitmap       = slot->bitmap;
          ft_glyph_top    = slot->bitmap_top;
          ft_glyph_left   = slot->bitmap_left;
      }
      else
      {
          FT_Stroker stroker;
          FT_BitmapGlyph ft_bitmap_glyph;
  
          error = FT_Stroker_New( library, &stroker );
  
          if( error )
          {
              fprintf(stderr, "FT_Error (0x%02x) : %s\n",
                      FT_Errors[error].code, FT_Errors[error].message);
              goto cleanup_stroker;
          }
  
          FT_Stroker_Set(stroker,
                          (int)(self->outline_thickness * HRES),
                          FT_STROKER_LINECAP_ROUND,
                          FT_STROKER_LINEJOIN_ROUND,
                          0);
  
          error = FT_Get_Glyph( face->glyph, &ft_glyph);
  
          if( error )
          {
              fprintf(stderr, "FT_Error (0x%02x) : %s\n",
                      FT_Errors[error].code, FT_Errors[error].message);
              goto cleanup_stroker;
          }
  
          if( self->rendermode == RENDER_OUTLINE_EDGE )
              error = FT_Glyph_Stroke( &ft_glyph, stroker, 1 );
          else if ( self->rendermode == RENDER_OUTLINE_POSITIVE )
              error = FT_Glyph_StrokeBorder( &ft_glyph, stroker, 0, 1 );
          else if ( self->rendermode == RENDER_OUTLINE_NEGATIVE )
              error = FT_Glyph_StrokeBorder( &ft_glyph, stroker, 1, 1 );
  
          if( error )
          {
              fprintf(stderr, "FT_Error (0x%02x) : %s\n",
                      FT_Errors[error].code, FT_Errors[error].message);
              goto cleanup_stroker;
          }
  
          if( self->atlas->depth == 1 )
              error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
          else
              error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_LCD, 0, 1);
  
          if( error )
          {
              fprintf(stderr, "FT_Error (0x%02x) : %s\n",
                      FT_Errors[error].code, FT_Errors[error].message);
              goto cleanup_stroker;
          }
  
          ft_bitmap_glyph = (FT_BitmapGlyph) ft_glyph;
          ft_bitmap       = ft_bitmap_glyph->bitmap;
          ft_glyph_top    = ft_bitmap_glyph->top;
          ft_glyph_left   = ft_bitmap_glyph->left;
  
  cleanup_stroker:
          FT_Stroker_Done( stroker );
  
          if( error )
          {
              FT_Done_Face( face );
              FT_Done_FreeType( library );
              return 0;
          }
      }
  
      struct {
          int left;
          int top;
          int right;
          int bottom;
      } padding = { 0, 0, 1, 1 };
  
      if( self->rendermode == RENDER_SIGNED_DISTANCE_FIELD )
      {
          padding.top = 1;
          padding.left = 1;
      }
  
      if( self->padding != 0 )
      {
          padding.top += self->padding;
          padding.left += self->padding;
          padding.right += self->padding;
          padding.bottom += self->padding;
      }
  
      size_t src_w = ft_bitmap.width/self->atlas->depth;
      size_t src_h = ft_bitmap.rows;
  
      size_t tgt_w = src_w + padding.left + padding.right;
      size_t tgt_h = src_h + padding.top + padding.bottom;
  
      region = texture_atlas_get_region( self->atlas, tgt_w, tgt_h );
  
      if ( region.x < 0 )
      {
          fprintf( stderr, "Texture atlas is full (line %d)\n",  __LINE__ );
          FT_Done_Face( face );
          FT_Done_FreeType( library );
          return 0;
      }
  
      x = region.x;
      y = region.y;
  
      unsigned char *buffer = calloc( tgt_w * tgt_h * self->atlas->depth, sizeof(unsigned char) );
  
      unsigned char *dst_ptr = buffer + (padding.top * tgt_w + padding.left) * self->atlas->depth;
      unsigned char *src_ptr = ft_bitmap.buffer;
      for( i = 0; i < src_h; i++ )
      {
          //difference between width and pitch: https://www.freetype.org/freetype2/docs/reference/ft2-basic_types.html#FT_Bitmap
          if (dst_ptr != 0) memcpy( dst_ptr, src_ptr, ft_bitmap.width);
          dst_ptr += tgt_w * self->atlas->depth;
          src_ptr += ft_bitmap.pitch;
      }
  
      if( self->rendermode == RENDER_SIGNED_DISTANCE_FIELD )
      {
          unsigned char *sdf = make_distance_mapb( buffer, (unsigned int)tgt_w, (unsigned int)tgt_h );
          free( buffer );
          buffer = sdf;
      }
  
      texture_atlas_set_region( self->atlas, x, y, tgt_w, tgt_h, buffer, tgt_w * self->atlas->depth);
  
      free( buffer );
  
      glyph = texture_glyph_new( );
      glyph->codepoint = utf8_to_utf32( codepoint );
      glyph->width    = tgt_w;
      glyph->height   = tgt_h;
      glyph->rendermode = self->rendermode;
      glyph->outline_thickness = self->outline_thickness;
      glyph->offset_x = ft_glyph_left;
      glyph->offset_y = ft_glyph_top;
      glyph->s0       = x/(float)self->atlas->width;
      glyph->t0       = y/(float)self->atlas->height;
      glyph->s1       = (x + glyph->width)/(float)self->atlas->width;
      glyph->t1       = (y + glyph->height)/(float)self->atlas->height;
  
      // Discard hinting to get advance
      FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER | FT_LOAD_NO_HINTING);
      slot = face->glyph;
      glyph->advance_x = slot->advance.x / HRESf;
      glyph->advance_y = slot->advance.y / HRESf;
  
      vector_push_back( self->glyphs, &glyph );
  
      if( self->rendermode != RENDER_NORMAL && self->rendermode != RENDER_SIGNED_DISTANCE_FIELD )
          FT_Done_Glyph( ft_glyph );
  
      texture_font_generate_kerning( self, &library, &face );
  
      FT_Done_Face( face );
      FT_Done_FreeType( library );
  
      return 1;
  }
  
  // ----------------------------------------------- texture_font_load_glyphs ---
  size_t
  texture_font_load_glyphs( texture_font_t * self,
                            const char * codepoints )
  {
      size_t i;
  
      /* Load each glyph */
      for( i = 0; i < strlen(codepoints); i += utf8_surrogate_len(codepoints + i) ) {
          if( !texture_font_load_glyph( self, codepoints + i ) )
              return utf8_strlen( codepoints + i );
      }
  
      return 0;
  }
  
  
  // ------------------------------------------------- texture_font_get_glyph ---
  texture_glyph_t *
  texture_font_get_glyph( texture_font_t * self,
                          const char * codepoint )
  {
      texture_glyph_t *glyph;
  
      assert( self );
      assert( self->filename );
      assert( self->atlas );
  
      /* Check if codepoint has been already loaded */
      if( (glyph = texture_font_find_glyph( self, codepoint )) )
          return glyph;
  
      /* Glyph has not been already loaded */
      if( texture_font_load_glyph( self, codepoint ) )
          return texture_font_find_glyph( self, codepoint );
  
      return NULL;
  }
  
  // ------------------------------------------------- texture_font_enlarge_atlas ---
  void
  texture_font_enlarge_atlas( texture_font_t * self, size_t width_new,
                              size_t height_new )
  {
      assert(self);
      assert(self->atlas);
      //ensure size increased
      assert(width_new >= self->atlas->width);
      assert(height_new >= self->atlas->height);
      assert(width_new + height_new > self->atlas->width + self->atlas->height);
      texture_atlas_t* ta = self->atlas;
      size_t width_old = ta->width;
      size_t height_old = ta->height;
      //allocate new buffer
      unsigned char* data_old = ta->data;
      ta->data = calloc(1,width_new*height_new * sizeof(char)*ta->depth);
      //update atlas size
      ta->width = width_new;
      ta->height = height_new;
      //add node reflecting the gained space on the right
      if( width_new > width_old )
      {
          ivec3 node;
          node.x = (int)width_old - 1;
          node.y = 1;
          node.z = (int)width_new - (int)width_old;
          vector_push_back(ta->nodes, &node);
      }
      //copy over data from the old buffer, skipping first row and column because of the margin
      size_t pixel_size = sizeof(char) * ta->depth;
      size_t old_row_size = width_old * pixel_size;
      texture_atlas_set_region(ta, 1, 1, width_old - 2, height_old - 2, data_old + old_row_size + pixel_size, old_row_size);
      free(data_old);
      //change uv coordinates of existing glyphs to reflect size change
      float mulw = (float)width_old / width_new;
      float mulh = (float)height_old / height_new;
      size_t i;
      for( i = 0; i < vector_size(self->glyphs); i++ )
      {
          texture_glyph_t* g = *(texture_glyph_t**)vector_get(self->glyphs, i);
          g->s0 *= mulw;
          g->s1 *= mulw;
          g->t0 *= mulh;
          g->t1 *= mulh;
      }
  }
  