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