1 /* Freetype GL - A C OpenGL Freetype engine
2  *
3  * Distributed under the OSI-approved BSD 2-Clause License.  See accompanying
4  * file `LICENSE` for more details.
5  */
6 #include <ft2build.h>
7 #include FT_FREETYPE_H
8 #include FT_STROKER_H
9 // #include FT_ADVANCES_H
10 #include FT_LCD_FILTER_H
11 #include <stdint.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <assert.h>
15 #include <math.h>
16 #include "distance-field.h"
17 #include "texture-font.h"
18 #include "platform.h"
19 #include "utf8-utils.h"
20 
21 #define HRES  64
22 #define HRESf 64.f
23 #define DPI   72
24 
convert_F26Dot6_to_float(FT_F26Dot6 value)25 static float convert_F26Dot6_to_float(FT_F26Dot6 value)
26 {
27   return ((float)value) / 64.0;
28 }
convert_float_to_F26Dot6(float value)29 static FT_F26Dot6 convert_float_to_F26Dot6(float value)
30 {
31   return (FT_F26Dot6) (value * 64.0);
32 }
33 
34 #undef FTERRORS_H_
35 #define FT_ERROR_START_LIST     switch ( error_code ) {
36 #define FT_ERRORDEF( e, v, s )    case v: return s;
37 #define FT_ERROR_END_LIST       }
38 // Same signature as the function defined in fterrors.h:
39 // https://www.freetype.org/freetype2/docs/reference/ft2-error_enumerations.html#ft_error_string
FT_Error_String(FT_Error error_code)40 const char* FT_Error_String( FT_Error error_code )
41 {
42 #include FT_ERRORS_H
43     return "INVALID ERROR CODE";
44 }
45 
46 // ------------------------------------------------- texture_font_load_face ---
47 static int
texture_font_load_face(texture_font_t * self,float size,FT_Library * library,FT_Face * face)48 texture_font_load_face(texture_font_t *self, float size,
49         FT_Library *library, FT_Face *face)
50 {
51     FT_Error error;
52     FT_Matrix matrix = {
53         (int)((1.0/HRES) * 0x10000L),
54         (int)((0.0)      * 0x10000L),
55         (int)((0.0)      * 0x10000L),
56         (int)((1.0)      * 0x10000L)};
57 
58     assert(library);
59     assert(size);
60 
61     /* Initialize library */
62     error = FT_Init_FreeType(library);
63     if( error )
64     {
65         fprintf( stderr, "FT_Error (line %d, 0x%02x) : %s\n",
66                  __LINE__, error, FT_Error_String(error) );
67         goto cleanup;
68     }
69 
70     /* Load face */
71     switch (self->location) {
72     case TEXTURE_FONT_FILE:
73         error = FT_New_Face(*library, self->filename, 0, face);
74         break;
75 
76     case TEXTURE_FONT_MEMORY:
77         error = FT_New_Memory_Face(*library,
78             self->memory.base, self->memory.size, 0, face);
79         break;
80     }
81 
82     if( error )
83     {
84         fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n",
85                  __LINE__, error, FT_Error_String(error) );
86         goto cleanup_library;
87     }
88 
89     /* Select charmap */
90     error = FT_Select_Charmap(*face, FT_ENCODING_UNICODE);
91     if( error )
92     {
93         fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n",
94                  __LINE__, error, FT_Error_String(error) );
95         goto cleanup_face;
96     }
97 
98     /* Set char size */
99     /* See page 24 of “Higher Quality 2D Text Rendering”:
100      * http://jcgt.org/published/0002/01/04/
101      * “To render high-quality text, Shemarev [2007] recommends using only
102      *  vertical hinting and completely discarding the horizontal hints.
103      *  Hinting is the responsibility of the rasterization engine (FreeType in
104      *  our case) which provides no option to specifically discard horizontal
105      *  hinting. In the case of the FreeType library, we can nonetheless trick
106      *  the engine by specifying an oversized horizontal DPI (100 times the
107      *  vertical) while specifying a transformation matrix that scale down the
108      *  glyph as shown in Listing 1.”
109      * That horizontal DPI factor is HRES here. */
110     error = FT_Set_Char_Size(*face, convert_float_to_F26Dot6(size), 0, DPI * HRES, DPI);
111 
112     if( error )
113     {
114         fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n",
115                  __LINE__, error, FT_Error_String(error) );
116         goto cleanup_face;
117     }
118 
119     /* Set transform matrix */
120     FT_Set_Transform(*face, &matrix, NULL);
121 
122     return 1;
123 
124 cleanup_face:
125     FT_Done_Face( *face );
126 cleanup_library:
127     FT_Done_FreeType( *library );
128 cleanup:
129     return 0;
130 }
131 
132 // ------------------------------------------------------ texture_glyph_new ---
133 texture_glyph_t *
texture_glyph_new(void)134 texture_glyph_new(void)
135 {
136     texture_glyph_t *self = (texture_glyph_t *) malloc( sizeof(texture_glyph_t) );
137     if(self == NULL) {
138         fprintf( stderr,
139                 "line %d: No more memory for allocating data\n", __LINE__);
140         return NULL;
141     }
142 
143     self->codepoint  = -1;
144     self->width     = 0;
145     self->height    = 0;
146     self->rendermode = RENDER_NORMAL;
147     self->outline_thickness = 0.0;
148     self->offset_x  = 0;
149     self->offset_y  = 0;
150     self->advance_x = 0.0;
151     self->advance_y = 0.0;
152     self->s0        = 0.0;
153     self->t0        = 0.0;
154     self->s1        = 0.0;
155     self->t1        = 0.0;
156     self->kerning   = vector_new( sizeof(kerning_t) );
157     return self;
158 }
159 
160 
161 // --------------------------------------------------- texture_glyph_delete ---
162 void
texture_glyph_delete(texture_glyph_t * self)163 texture_glyph_delete( texture_glyph_t *self )
164 {
165     assert( self );
166     vector_delete( self->kerning );
167     free( self );
168 }
169 
170 // ---------------------------------------------- texture_glyph_get_kerning ---
171 float
texture_glyph_get_kerning(const texture_glyph_t * self,const char * codepoint)172 texture_glyph_get_kerning( const texture_glyph_t * self,
173                            const char * codepoint )
174 {
175     size_t i;
176     uint32_t ucodepoint = utf8_to_utf32( codepoint );
177 
178     assert( self );
179     for( i=0; i<vector_size(self->kerning); ++i )
180     {
181         kerning_t * kerning = (kerning_t *) vector_get( self->kerning, i );
182         if( kerning->codepoint == ucodepoint )
183         {
184             return kerning->kerning;
185         }
186     }
187     return 0;
188 }
189 
190 
191 // ------------------------------------------ texture_font_generate_kerning ---
192 void
texture_font_generate_kerning(texture_font_t * self,FT_Library * library,FT_Face * face)193 texture_font_generate_kerning( texture_font_t *self,
194                                FT_Library *library, FT_Face *face )
195 {
196     size_t i, j;
197     FT_UInt glyph_index, prev_index;
198     texture_glyph_t *glyph, *prev_glyph;
199     FT_Vector kerning;
200 
201     assert( self );
202 
203     /* For each glyph couple combination, check if kerning is necessary */
204     /* Starts at index 1 since 0 is for the special backgroudn glyph */
205     for( i=1; i<self->glyphs->size; ++i )
206     {
207         glyph = *(texture_glyph_t **) vector_get( self->glyphs, i );
208         glyph_index = FT_Get_Char_Index( *face, glyph->codepoint );
209         vector_clear( glyph->kerning );
210 
211         for( j=1; j<self->glyphs->size; ++j )
212         {
213             prev_glyph = *(texture_glyph_t **) vector_get( self->glyphs, j );
214             prev_index = FT_Get_Char_Index( *face, prev_glyph->codepoint );
215             // FT_KERNING_UNFITTED returns FT_F26Dot6 values.
216             FT_Get_Kerning( *face, prev_index, glyph_index, FT_KERNING_UNFITTED, &kerning );
217             // printf("%c(%d)-%c(%d): %ld\n",
218             //       prev_glyph->codepoint, prev_glyph->codepoint,
219             //       glyph_index, glyph_index, kerning.x);
220             if( kerning.x )
221             {
222                 kerning_t k = {
223                   prev_glyph->codepoint,
224                   convert_F26Dot6_to_float(kerning.x) / HRESf
225                 };
226                 vector_push_back( glyph->kerning, &k );
227             }
228         }
229     }
230 }
231 
232 // ------------------------------------------------------ texture_font_init ---
233 static int
texture_font_init(texture_font_t * self)234 texture_font_init(texture_font_t *self)
235 {
236     FT_Library library;
237     FT_Face face;
238     FT_Size_Metrics metrics;
239 
240     assert(self->atlas);
241     assert(self->size > 0);
242     assert((self->location == TEXTURE_FONT_FILE && self->filename)
243         || (self->location == TEXTURE_FONT_MEMORY
244             && self->memory.base && self->memory.size));
245 
246     self->glyphs = vector_new(sizeof(texture_glyph_t *));
247     self->height = 0;
248     self->ascender = 0;
249     self->descender = 0;
250     self->rendermode = RENDER_NORMAL;
251     self->outline_thickness = 0.0;
252     self->hinting = 1;
253     self->kerning = 1;
254     self->filtering = 1;
255 
256     // FT_LCD_FILTER_LIGHT   is (0x00, 0x55, 0x56, 0x55, 0x00)
257     // FT_LCD_FILTER_DEFAULT is (0x10, 0x40, 0x70, 0x40, 0x10)
258     self->lcd_weights[0] = 0x10;
259     self->lcd_weights[1] = 0x40;
260     self->lcd_weights[2] = 0x70;
261     self->lcd_weights[3] = 0x40;
262     self->lcd_weights[4] = 0x10;
263 
264     if (!texture_font_load_face(self, self->size, &library, &face))
265         return -1;
266 
267     self->underline_position = face->underline_position / (float)(HRESf*HRESf) * self->size;
268     self->underline_position = roundf( self->underline_position );
269     if( self->underline_position > -2 )
270     {
271         self->underline_position = -2.0;
272     }
273 
274     self->underline_thickness = face->underline_thickness / (float)(HRESf*HRESf) * self->size;
275     self->underline_thickness = roundf( self->underline_thickness );
276     if( self->underline_thickness < 1 )
277     {
278         self->underline_thickness = 1.0;
279     }
280 
281     metrics = face->size->metrics;
282     self->ascender  = metrics.ascender  >> 6;
283     self->descender = metrics.descender >> 6;
284     self->height    = metrics.height    >> 6;
285     self->linegap = self->height - self->ascender + self->descender;
286     FT_Done_Face( face );
287     FT_Done_FreeType( library );
288 
289     /* NULL is a special glyph */
290     texture_font_get_glyph( self, NULL );
291 
292     return 0;
293 }
294 
295 // --------------------------------------------- texture_font_new_from_file ---
296 texture_font_t *
texture_font_new_from_file(texture_atlas_t * atlas,const float pt_size,const char * filename)297 texture_font_new_from_file(texture_atlas_t *atlas, const float pt_size,
298         const char *filename)
299 {
300     texture_font_t *self;
301 
302     assert(filename);
303 
304     self = calloc(1, sizeof(*self));
305     if (!self) {
306         fprintf(stderr,
307                 "line %d: No more memory for allocating data\n", __LINE__);
308         return NULL;
309     }
310 
311     self->atlas = atlas;
312     self->size  = pt_size;
313 
314     self->location = TEXTURE_FONT_FILE;
315     self->filename = strdup(filename);
316 
317     if (texture_font_init(self)) {
318         texture_font_delete(self);
319         return NULL;
320     }
321 
322     return self;
323 }
324 
325 // ------------------------------------------- texture_font_new_from_memory ---
326 texture_font_t *
texture_font_new_from_memory(texture_atlas_t * atlas,float pt_size,const void * memory_base,size_t memory_size)327 texture_font_new_from_memory(texture_atlas_t *atlas, float pt_size,
328         const void *memory_base, size_t memory_size)
329 {
330     texture_font_t *self;
331 
332     assert(memory_base);
333     assert(memory_size);
334 
335     self = calloc(1, sizeof(*self));
336     if (!self) {
337         fprintf(stderr,
338                 "line %d: No more memory for allocating data\n", __LINE__);
339         return NULL;
340     }
341 
342     self->atlas = atlas;
343     self->size  = pt_size;
344 
345     self->location = TEXTURE_FONT_MEMORY;
346     self->memory.base = memory_base;
347     self->memory.size = memory_size;
348 
349     if (texture_font_init(self)) {
350         texture_font_delete(self);
351         return NULL;
352     }
353 
354     return self;
355 }
356 
357 // ---------------------------------------------------- texture_font_delete ---
358 void
texture_font_delete(texture_font_t * self)359 texture_font_delete( texture_font_t *self )
360 {
361     size_t i;
362     texture_glyph_t *glyph;
363 
364     assert( self );
365 
366     if(self->location == TEXTURE_FONT_FILE && self->filename)
367         free( self->filename );
368 
369     for( i=0; i<vector_size( self->glyphs ); ++i)
370     {
371         glyph = *(texture_glyph_t **) vector_get( self->glyphs, i );
372         texture_glyph_delete( glyph);
373     }
374 
375     vector_delete( self->glyphs );
376     free( self );
377 }
378 
379 texture_glyph_t *
texture_font_find_glyph(texture_font_t * self,const char * codepoint)380 texture_font_find_glyph( texture_font_t * self,
381                          const char * codepoint )
382 {
383     size_t i;
384     texture_glyph_t *glyph;
385     uint32_t ucodepoint = utf8_to_utf32( codepoint );
386 
387     for( i = 0; i < self->glyphs->size; ++i )
388     {
389         glyph = *(texture_glyph_t **) vector_get( self->glyphs, i );
390         // If codepoint is -1, we don't care about outline type or thickness
391         if( (glyph->codepoint == ucodepoint) &&
392             ((ucodepoint == -1) ||
393              ((glyph->rendermode == self->rendermode) &&
394               (glyph->outline_thickness == self->outline_thickness)) ))
395         {
396             return glyph;
397         }
398     }
399 
400     return NULL;
401 }
402 
403 // ------------------------------------------------ texture_font_load_glyph ---
404 int
texture_font_load_glyph(texture_font_t * self,const char * codepoint)405 texture_font_load_glyph( texture_font_t * self,
406                          const char * codepoint )
407 {
408     size_t i, x, y;
409 
410     FT_Library library;
411     FT_Error error;
412     FT_Face face;
413     FT_Glyph ft_glyph;
414     FT_GlyphSlot slot;
415     FT_Bitmap ft_bitmap;
416 
417     FT_UInt glyph_index;
418     texture_glyph_t *glyph;
419     FT_Int32 flags = 0;
420     int ft_glyph_top = 0;
421     int ft_glyph_left = 0;
422 
423     ivec4 region;
424     size_t missed = 0;
425 
426 
427     if (!texture_font_load_face(self, self->size, &library, &face))
428         return 0;
429 
430     /* Check if codepoint has been already loaded */
431     if (texture_font_find_glyph(self, codepoint)) {
432         FT_Done_Face(face);
433         FT_Done_FreeType(library);
434         return 1;
435     }
436 
437     /* codepoint NULL is special : it is used for line drawing (overline,
438      * underline, strikethrough) and background.
439      */
440     if( !codepoint )
441     {
442         ivec4 region = texture_atlas_get_region( self->atlas, 5, 5 );
443         texture_glyph_t * glyph = texture_glyph_new( );
444         static unsigned char data[4*4*3] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
445                                             -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
446                                             -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
447                                             -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
448         if ( region.x < 0 )
449         {
450             fprintf( stderr, "Texture atlas is full (line %d)\n",  __LINE__ );
451             FT_Done_Face( face );
452             FT_Done_FreeType( library );
453             return 0;
454         }
455         texture_atlas_set_region( self->atlas, region.x, region.y, 4, 4, data, 0 );
456         glyph->codepoint = -1;
457         glyph->s0 = (region.x+2)/(float)self->atlas->width;
458         glyph->t0 = (region.y+2)/(float)self->atlas->height;
459         glyph->s1 = (region.x+3)/(float)self->atlas->width;
460         glyph->t1 = (region.y+3)/(float)self->atlas->height;
461         vector_push_back( self->glyphs, &glyph );
462 
463         FT_Done_Face(face);
464         FT_Done_FreeType(library);
465         return 1;
466     }
467 
468     flags = 0;
469     ft_glyph_top = 0;
470     ft_glyph_left = 0;
471     glyph_index = FT_Get_Char_Index( face, (FT_ULong)utf8_to_utf32( codepoint ) );
472     // WARNING: We use texture-atlas depth to guess if user wants
473     //          LCD subpixel rendering
474 
475     if( self->rendermode != RENDER_NORMAL && self->rendermode != RENDER_SIGNED_DISTANCE_FIELD )
476     {
477         flags |= FT_LOAD_NO_BITMAP;
478     }
479     else
480     {
481         flags |= FT_LOAD_RENDER;
482     }
483 
484     if( !self->hinting )
485     {
486         flags |= FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT;
487     }
488     else
489     {
490         flags |= FT_LOAD_FORCE_AUTOHINT;
491     }
492 
493     if( self->atlas->depth == 3 )
494     {
495         FT_Library_SetLcdFilter( library, FT_LCD_FILTER_LIGHT );
496         flags |= FT_LOAD_TARGET_LCD;
497 
498         if( self->filtering )
499         {
500             FT_Library_SetLcdFilterWeights( library, self->lcd_weights );
501         }
502     }
503     else if (HRES == 1)
504     {
505         /* “FT_LOAD_TARGET_LIGHT
506          *  A lighter hinting algorithm for gray-level modes. Many generated
507          *  glyphs are fuzzier but better resemble their original shape.
508          *  This is achieved by snapping glyphs to the pixel grid
509          *  only vertically (Y-axis), as is done by FreeType's new CFF engine
510          *  or Microsoft's ClearType font renderer.”
511          * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_target_xxx
512          */
513         flags |= FT_LOAD_TARGET_LIGHT;
514     }
515 
516     error = FT_Load_Glyph( face, glyph_index, flags );
517     if( error )
518     {
519         fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n",
520                  __LINE__, error, FT_Error_String(error) );
521         FT_Done_Face( face );
522         FT_Done_FreeType( library );
523         return 0;
524     }
525 
526     if( self->rendermode == RENDER_NORMAL || self->rendermode == RENDER_SIGNED_DISTANCE_FIELD )
527     {
528         slot            = face->glyph;
529         ft_bitmap       = slot->bitmap;
530         ft_glyph_top    = slot->bitmap_top;
531         ft_glyph_left   = slot->bitmap_left;
532     }
533     else
534     {
535         FT_Stroker stroker;
536         FT_BitmapGlyph ft_bitmap_glyph;
537 
538         error = FT_Stroker_New( library, &stroker );
539 
540         if( error )
541         {
542             fprintf( stderr, "FT_Error (line %d, 0x%02x) : %s\n",
543                      __LINE__, error, FT_Error_String(error) );
544             goto cleanup_stroker;
545         }
546 
547         FT_Stroker_Set(stroker,
548                         (int)(self->outline_thickness * HRES),
549                         FT_STROKER_LINECAP_ROUND,
550                         FT_STROKER_LINEJOIN_ROUND,
551                         0);
552 
553         error = FT_Get_Glyph( face->glyph, &ft_glyph);
554 
555         if( error )
556         {
557             fprintf( stderr, "FT_Error (line %d, 0x%02x) : %s\n",
558                      __LINE__, error, FT_Error_String(error) );
559             goto cleanup_stroker;
560         }
561 
562         if( self->rendermode == RENDER_OUTLINE_EDGE )
563             error = FT_Glyph_Stroke( &ft_glyph, stroker, 1 );
564         else if ( self->rendermode == RENDER_OUTLINE_POSITIVE )
565             error = FT_Glyph_StrokeBorder( &ft_glyph, stroker, 0, 1 );
566         else if ( self->rendermode == RENDER_OUTLINE_NEGATIVE )
567             error = FT_Glyph_StrokeBorder( &ft_glyph, stroker, 1, 1 );
568 
569         if( error )
570         {
571             fprintf( stderr, "FT_Error (line %d, 0x%02x) : %s\n",
572                      __LINE__, error, FT_Error_String(error) );
573             goto cleanup_stroker;
574         }
575 
576         if( self->atlas->depth == 1 )
577             error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
578         else
579             error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_LCD, 0, 1);
580 
581         if( error )
582         {
583             fprintf( stderr, "FT_Error (line %d, 0x%02x) : %s\n",
584                      __LINE__, error, FT_Error_String(error) );
585             goto cleanup_stroker;
586         }
587 
588         ft_bitmap_glyph = (FT_BitmapGlyph) ft_glyph;
589         ft_bitmap       = ft_bitmap_glyph->bitmap;
590         ft_glyph_top    = ft_bitmap_glyph->top;
591         ft_glyph_left   = ft_bitmap_glyph->left;
592 
593 cleanup_stroker:
594         FT_Stroker_Done( stroker );
595 
596         if( error )
597         {
598             FT_Done_Face( face );
599             FT_Done_FreeType( library );
600             return 0;
601         }
602     }
603 
604     struct {
605         int left;
606         int top;
607         int right;
608         int bottom;
609     } padding = { 0, 0, 1, 1 };
610 
611     if( self->rendermode == RENDER_SIGNED_DISTANCE_FIELD )
612     {
613         padding.top = 1;
614         padding.left = 1;
615     }
616 
617     if( self->padding != 0 )
618     {
619         padding.top += self->padding;
620         padding.left += self->padding;
621         padding.right += self->padding;
622         padding.bottom += self->padding;
623     }
624 
625     size_t src_w = ft_bitmap.width/self->atlas->depth;
626     size_t src_h = ft_bitmap.rows;
627 
628     size_t tgt_w = src_w + padding.left + padding.right;
629     size_t tgt_h = src_h + padding.top + padding.bottom;
630 
631     region = texture_atlas_get_region( self->atlas, tgt_w, tgt_h );
632 
633     if ( region.x < 0 )
634     {
635         fprintf( stderr, "Texture atlas is full (line %d)\n",  __LINE__ );
636         FT_Done_Face( face );
637         FT_Done_FreeType( library );
638         return 0;
639     }
640 
641     x = region.x;
642     y = region.y;
643 
644     unsigned char *buffer = calloc( tgt_w * tgt_h * self->atlas->depth, sizeof(unsigned char) );
645 
646     unsigned char *dst_ptr = buffer + (padding.top * tgt_w + padding.left) * self->atlas->depth;
647     unsigned char *src_ptr = ft_bitmap.buffer;
648     for( i = 0; i < src_h; i++ )
649     {
650         //difference between width and pitch: https://www.freetype.org/freetype2/docs/reference/ft2-basic_types.html#FT_Bitmap
651         memcpy( dst_ptr, src_ptr, ft_bitmap.width);
652         dst_ptr += tgt_w * self->atlas->depth;
653         src_ptr += ft_bitmap.pitch;
654     }
655 
656     if( self->rendermode == RENDER_SIGNED_DISTANCE_FIELD )
657     {
658         unsigned char *sdf = make_distance_mapb( buffer, tgt_w, tgt_h );
659         free( buffer );
660         buffer = sdf;
661     }
662 
663     texture_atlas_set_region( self->atlas, x, y, tgt_w, tgt_h, buffer, tgt_w * self->atlas->depth);
664 
665     free( buffer );
666 
667     glyph = texture_glyph_new( );
668     glyph->codepoint = utf8_to_utf32( codepoint );
669     glyph->width    = tgt_w;
670     glyph->height   = tgt_h;
671     glyph->rendermode = self->rendermode;
672     glyph->outline_thickness = self->outline_thickness;
673     glyph->offset_x = ft_glyph_left;
674     glyph->offset_y = ft_glyph_top;
675     glyph->s0       = x/(float)self->atlas->width;
676     glyph->t0       = y/(float)self->atlas->height;
677     glyph->s1       = (x + glyph->width)/(float)self->atlas->width;
678     glyph->t1       = (y + glyph->height)/(float)self->atlas->height;
679 
680     // Discard hinting to get advance
681     FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER | FT_LOAD_NO_HINTING);
682     slot = face->glyph;
683     glyph->advance_x = convert_F26Dot6_to_float(slot->advance.x);
684     glyph->advance_y = convert_F26Dot6_to_float(slot->advance.y);
685 
686     vector_push_back( self->glyphs, &glyph );
687 
688     if( self->rendermode != RENDER_NORMAL && self->rendermode != RENDER_SIGNED_DISTANCE_FIELD )
689         FT_Done_Glyph( ft_glyph );
690 
691     texture_font_generate_kerning( self, &library, &face );
692 
693     FT_Done_Face( face );
694     FT_Done_FreeType( library );
695 
696     return 1;
697 }
698 
699 // ----------------------------------------------- texture_font_load_glyphs ---
700 size_t
texture_font_load_glyphs(texture_font_t * self,const char * codepoints)701 texture_font_load_glyphs( texture_font_t * self,
702                           const char * codepoints )
703 {
704     size_t i, c;
705 
706     /* Load each glyph */
707     for( i = 0; i < strlen(codepoints); i += utf8_surrogate_len(codepoints + i) ) {
708         if( !texture_font_load_glyph( self, codepoints + i ) )
709             return utf8_strlen( codepoints + i );
710     }
711 
712     return 0;
713 }
714 
715 
716 // ------------------------------------------------- texture_font_get_glyph ---
717 texture_glyph_t *
texture_font_get_glyph(texture_font_t * self,const char * codepoint)718 texture_font_get_glyph( texture_font_t * self,
719                         const char * codepoint )
720 {
721     texture_glyph_t *glyph;
722 
723     assert( self );
724     assert( self->filename );
725     assert( self->atlas );
726 
727     /* Check if codepoint has been already loaded */
728     if( (glyph = texture_font_find_glyph( self, codepoint )) )
729         return glyph;
730 
731     /* Glyph has not been already loaded */
732     if( texture_font_load_glyph( self, codepoint ) )
733         return texture_font_find_glyph( self, codepoint );
734 
735     return NULL;
736 }
737 
738 // ------------------------------------------------- texture_font_enlarge_atlas ---
739 void
texture_font_enlarge_atlas(texture_font_t * self,size_t width_new,size_t height_new)740 texture_font_enlarge_atlas( texture_font_t * self, size_t width_new,
741                             size_t height_new )
742 {
743     assert(self);
744     assert(self->atlas);
745     //ensure size increased
746     assert(width_new >= self->atlas->width);
747     assert(height_new >= self->atlas->height);
748     assert(width_new + height_new > self->atlas->width + self->atlas->height);
749     texture_atlas_t* ta = self->atlas;
750     size_t width_old = ta->width;
751     size_t height_old = ta->height;
752     //allocate new buffer
753     unsigned char* data_old = ta->data;
754     ta->data = calloc(1,width_new*height_new * sizeof(char)*ta->depth);
755     //update atlas size
756     ta->width = width_new;
757     ta->height = height_new;
758     //add node reflecting the gained space on the right
759     if( width_new > width_old )
760     {
761         ivec3 node;
762         node.x = width_old - 1;
763         node.y = 1;
764         node.z = width_new - width_old;
765         vector_push_back(ta->nodes, &node);
766     }
767     //copy over data from the old buffer, skipping first row and column because of the margin
768     size_t pixel_size = sizeof(char) * ta->depth;
769     size_t old_row_size = width_old * pixel_size;
770     texture_atlas_set_region(ta, 1, 1, width_old - 2, height_old - 2, data_old + old_row_size + pixel_size, old_row_size);
771     free(data_old);
772     //change uv coordinates of existing glyphs to reflect size change
773     float mulw = (float)width_old / width_new;
774     float mulh = (float)height_old / height_new;
775     size_t i;
776     for( i = 0; i < vector_size(self->glyphs); i++ )
777     {
778         texture_glyph_t* g = *(texture_glyph_t**)vector_get(self->glyphs, i);
779         g->s0 *= mulw;
780         g->s1 *= mulw;
781         g->t0 *= mulh;
782         g->t1 *= mulh;
783     }
784 }
785