texture-font.c 30.7 KB
Newer Older
1
/* Freetype GL - A C OpenGL Freetype engine
Nicolas.Rougier's avatar
Nicolas.Rougier committed
2
 *
3 4
 * Distributed under the OSI-approved BSD 2-Clause License.  See accompanying
 * file `LICENSE` for more details.
5
 */
Nicolas.Rougier's avatar
Nicolas.Rougier committed
6 7
#include <ft2build.h>
#include FT_FREETYPE_H
8
#include FT_SIZES_H
9
#include FT_STROKER_H
10
// #include FT_ADVANCES_H
11
#include FT_LCD_FILTER_H
12
#include FT_TRUETYPE_TABLES_H
Nicolas.Rougier's avatar
Nicolas.Rougier committed
13 14 15 16 17
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <math.h>
Bernd Paysan's avatar
Bernd Paysan committed
18 19 20 21 22
#ifdef __APPLE__
# include <machine/endian.h>
#else
# include <endian.h>
#endif
23
#include "distance-field.h"
Nicolas.Rougier's avatar
Nicolas.Rougier committed
24
#include "texture-font.h"
25
#include "platform.h"
26
#include "utf8-utils.h"
27
#include "freetype-gl-err.h"
Nicolas.Rougier's avatar
Nicolas.Rougier committed
28

29 30 31 32
#define HRES  64
#define HRESf 64.f
#define DPI   72

Nicolas.Rougier's avatar
Nicolas.Rougier committed
33 34 35 36 37 38 39 40 41 42
#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

43
// per-thread library
44

45
__THREAD texture_font_library_t * freetype_gl_library = NULL;
Bernd Paysan's avatar
Bernd Paysan committed
46
__THREAD font_mode_t mode_default=MODE_AUTO_CLOSE;
Nicolas.Rougier's avatar
Nicolas.Rougier committed
47

48 49 50 51 52 53 54 55 56 57 58 59
// rol8 ror8

static inline uint32_t rol8(uint32_t in)
{
    return (in >> 24) | (in << 8);
}

static inline uint32_t ror8(uint32_t in)
{
    return (in >> 8) | (in << 24);
}

60 61
// ------------------------------------------------------ texture_glyph_new ---
texture_glyph_t *
62
texture_glyph_new(void)
63 64
{
    texture_glyph_t *self = (texture_glyph_t *) malloc( sizeof(texture_glyph_t) );
65
    if(self == NULL) {
66
        freetype_gl_error( Out_Of_Memory,
Bernd Paysan's avatar
Bernd Paysan committed
67
			   "%s:%d: No more memory for allocating data\n", __FILENAME__, __LINE__);
68
        return NULL;
69
    }
70

71
    self->codepoint  = -1;
72 73
    self->width     = 0;
    self->height    = 0;
74
    /* Attributes that can have different images for the same codepoint */
75
    self->rendermode = RENDER_NORMAL;
76
    self->outline_thickness = 0.0;
77 78
    self->glyphmode = GLYPH_END;
    /* End of attribute part */
79 80 81 82 83 84 85 86
    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;
Bernd Paysan's avatar
Bernd Paysan committed
87
    self->kerning   = vector_new( sizeof(float**) );
88 89
    return self;
}
Nicolas.Rougier's avatar
Nicolas.Rougier committed
90

91
// ---------------------------------------------- texture_font_default_mode ---
Bernd Paysan's avatar
Bernd Paysan committed
92
void
93
texture_font_default_mode(font_mode_t mode)
Bernd Paysan's avatar
Bernd Paysan committed
94 95 96
{
    mode_default=mode;
}
97 98

// --------------------------------------------------- texture_glyph_delete ---
Nicolas.Rougier's avatar
Nicolas.Rougier committed
99
void
100
texture_glyph_delete( texture_glyph_t *self )
Nicolas.Rougier's avatar
Nicolas.Rougier committed
101
{
Bernd Paysan's avatar
Bernd Paysan committed
102
    int i;
Nicolas.Rougier's avatar
Nicolas.Rougier committed
103
    assert( self );
Bernd Paysan's avatar
Bernd Paysan committed
104 105
    for(i=0; i < self->kerning->size; i++)
	free( *(float **) vector_get( self->kerning, i ) );
106
    vector_delete( self->kerning );
Nicolas.Rougier's avatar
Nicolas.Rougier committed
107 108 109
    free( self );
}

110
// ---------------------------------------------- texture_glyph_get_kerning ---
111
float
112
texture_glyph_get_kerning( const texture_glyph_t * self,
113
                           const char * codepoint )
114
{
115
    uint32_t ucodepoint = utf8_to_utf32( codepoint );
Bernd Paysan's avatar
Bernd Paysan committed
116 117 118
    uint32_t i = ucodepoint >> 8;
    uint32_t j = ucodepoint & 0xFF;
    float *kern_index;
Nicolas.Rougier's avatar
Nicolas.Rougier committed
119

120
    assert( self );
Bernd Paysan's avatar
Bernd Paysan committed
121 122 123 124 125 126 127 128 129 130 131
    if(ucodepoint == -1)
	return 0;
    if(self->kerning->size <= i)
	return 0;

    kern_index = *(float **) vector_get( self->kerning, i );

    if(!kern_index)
	return 0;
    else
	return kern_index[j];
132
}
Nicolas.Rougier's avatar
Nicolas.Rougier committed
133

Bernd Paysan's avatar
Bernd Paysan committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
// ---------------------------------------------- texture_font_index_kerning ---

void texture_font_index_kerning( texture_glyph_t * self,
				 uint32_t codepoint,
				 float kerning)
{
    uint32_t i = codepoint >> 8;
    uint32_t j = codepoint & 0xFF;
    float ** kerning_index;

    if(self->kerning->size <= i) {
	vector_resize( self->kerning, i+1);
    }

    kerning_index = (float **) vector_get( self->kerning, i );

    if(!*kerning_index) {
	*kerning_index = calloc( 0x100, sizeof(float) );
    }

    (*kerning_index)[j] = kerning;
}
156 157

// ------------------------------------------ texture_font_generate_kerning ---
Nicolas.Rougier's avatar
Nicolas.Rougier committed
158
void
159 160
texture_font_generate_kerning( texture_font_t *self,
                               FT_Library *library, FT_Face *face )
Nicolas.Rougier's avatar
Nicolas.Rougier committed
161
{
Bernd Paysan's avatar
Bernd Paysan committed
162
    size_t i, j, k;
163 164 165
    FT_UInt glyph_index, prev_index;
    texture_glyph_t *glyph, *prev_glyph;
    FT_Vector kerning;
166

167
    assert( self );
168 169

    /* For each glyph couple combination, check if kerning is necessary */
Bernd Paysan's avatar
Bernd Paysan committed
170 171 172 173 174
    /* Starts at index 1 since 0 is for the special background glyph */
    GLYPHS_ITERATOR(i, glyph, self->glyphs ) {
	glyph_index = FT_Get_Char_Index( *face, glyph->codepoint );
//	fprintf(stderr, "Retrieving glyph %p from index %i\n", __glyphs, __i);
//	fprintf(stderr, "Glpyh %p: Indexing %d, kerning %p\n", glyph, glyph_index, glyph->kerning);
Bernd Paysan's avatar
Bernd Paysan committed
175 176
	for(k=0; k < glyph->kerning->size; k++)
	    free( *(float **) vector_get( glyph->kerning, k ) );
Bernd Paysan's avatar
Bernd Paysan committed
177 178 179 180 181 182 183 184 185
	vector_clear( glyph->kerning );
	
	GLYPHS_ITERATOR(j, prev_glyph, self->glyphs ) {
	    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 ) {
Bernd Paysan's avatar
Bernd Paysan committed
186 187 188
		texture_font_index_kerning( glyph,
					    prev_glyph->codepoint,
					    kerning.x / (float)(HRESf*HRESf) );
Bernd Paysan's avatar
Bernd Paysan committed
189 190 191 192
	    }
	    // also insert kerning with the current added element
	    FT_Get_Kerning( *face, glyph_index, prev_index, FT_KERNING_UNFITTED, &kerning );
	    if( kerning.x ) {
Bernd Paysan's avatar
Bernd Paysan committed
193 194 195
		texture_font_index_kerning( prev_glyph,
					    glyph->codepoint,
					    kerning.x / (float)(HRESf*HRESf) );
Bernd Paysan's avatar
Bernd Paysan committed
196
	    }
Nicolas.Rougier's avatar
Nicolas.Rougier committed
197
        }
Bernd Paysan's avatar
Bernd Paysan committed
198
        GLYPHS_ITERATOR_END
Nicolas.Rougier's avatar
Nicolas.Rougier committed
199
    }
Bernd Paysan's avatar
Bernd Paysan committed
200
    GLYPHS_ITERATOR_END
201 202
}

Bernd Paysan's avatar
Bernd Paysan committed
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
// -------------------------------------------------- texture_is_color_font ---

int
texture_is_color_font( texture_font_t *self) {
    static const uint32_t tag = FT_MAKE_TAG('C', 'B', 'D', 'T');
    unsigned long length = 0;
    FT_Load_Sfnt_Table(self->face, tag, 0, NULL, &length);
    return length != 0;
}

// -------------------------------------------------- texture_font_set_size ---

int
texture_font_set_size ( texture_font_t *self, float size )
{
    FT_Error error=0;
    FT_Matrix matrix = {
        (int)((1.0/HRES) * 0x10000L),
        (int)((0.0)      * 0x10000L),
        (int)((0.0)      * 0x10000L),
        (int)((1.0)      * 0x10000L)};

    if( texture_is_color_font( self ) ) {
	/* Select best size */
	if (self->face->num_fixed_sizes == 0) {
	    freetype_error( error, "FT_Error (%s:%d) : no fixed size in color font\n",
			    __FILENAME__, __LINE__);
	    return 0;
	}
	
	int best_match = 0;
	int diff = abs((int)size - self->face->available_sizes[0].width);
Bernd Paysan's avatar
Bernd Paysan committed
235
	int i;
Bernd Paysan's avatar
Bernd Paysan committed
236

Bernd Paysan's avatar
Bernd Paysan committed
237
	for (i = 1; i < self->face->num_fixed_sizes; ++i) {
Bernd Paysan's avatar
Bernd Paysan committed
238 239 240 241 242 243 244 245 246 247 248 249
	    int ndiff = abs((int)size - self->face->available_sizes[i].width);
	    if (ndiff < diff) {
		best_match = i;
		diff = ndiff;
	    }
	}
	error = FT_Select_Size(self->face, best_match);
	if(error) {
	    freetype_error( error, "FT_Error (%s:%d, code 0x%02x) : %s\n",
			    __FILENAME__, __LINE__, FT_Errors[error].code, FT_Errors[error].message);
	    return 0;
	}
250
	self->scale = self->size / self->face->available_sizes[best_match].width;
Bernd Paysan's avatar
Bernd Paysan committed
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
    } else {
	/* Set char size */
	error = FT_Set_Char_Size(self->face, (int)(size * HRES), 0, DPI * HRES, DPI);
	
	if(error) {
	    freetype_error( error, "FT_Error (%s:%d, code 0x%02x) : %s\n",
			    __FILENAME__, __LINE__, FT_Errors[error].code, FT_Errors[error].message);
	    return 0;
	}
    }
    /* Set transform matrix */
    FT_Set_Transform(self->face, &matrix, NULL);

    return 1;
}

Bernd Paysan's avatar
Bernd Paysan committed
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
// --------------------------------------------------

void
texture_font_init_size( texture_font_t * self)
{
    FT_Size_Metrics metrics;
    
    self->underline_position = self->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 = self->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 = self->face->size->metrics;
    self->ascender = (metrics.ascender >> 6) / 100.f;
    self->descender = (metrics.descender >> 6) / 100.f;
    self->height = (metrics.height >> 6) / 100.f;
    self->linegap = self->height - self->ascender + self->descender;
}

295 296 297
// ------------------------------------------------------ texture_font_init ---
static int
texture_font_init(texture_font_t *self)
298
{
299 300 301 302 303 304 305
    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 *));
306 307 308
    self->height = 0;
    self->ascender = 0;
    self->descender = 0;
309
    self->linegap = 0;
310
    self->rendermode = RENDER_NORMAL;
311
    self->outline_thickness = 0.0;
312
    self->hinting = 1;
313
    self->kerning = 1;
314
    self->filtering = 1;
315
    self->scaletex = 1;
316
    self->scale = 1.0;
317

318 319 320 321 322 323 324 325
    // 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;

Bernd Paysan's avatar
Bernd Paysan committed
326
    if (!texture_font_load_face(self, self->size * 100.f))
327
        return -1;
328

Bernd Paysan's avatar
Bernd Paysan committed
329
    texture_font_init_size( self );
330

Bernd Paysan's avatar
Bernd Paysan committed
331 332 333
    if (!texture_font_set_size(self, self->size))
	return -1;
    
334 335
    /* NULL is a special glyph */
    texture_font_get_glyph( self, NULL );
336

337 338 339
    return 0;
}

340 341 342 343 344 345
// ---------------------------------------------------- texture_library_new ---
texture_font_library_t *
texture_library_new()
{
    texture_font_library_t *self = calloc(1, sizeof(*self));
    
Bernd Paysan's avatar
Bernd Paysan committed
346
    self->mode = MODE_ALWAYS_OPEN;
347 348 349 350
    
    return self;
}

351 352
// --------------------------------------------- texture_font_new_from_file ---
texture_font_t *
353
texture_font_new_from_file(texture_atlas_t *atlas, const float pt_size,
354
			   const char *filename)
355 356 357 358 359 360 361
{
    texture_font_t *self;

    assert(filename);

    self = calloc(1, sizeof(*self));
    if (!self) {
362
        freetype_gl_error( Out_Of_Memory,
Bernd Paysan's avatar
Bernd Paysan committed
363
			   "%s:%d: No more memory for allocating data\n", __FILENAME__, __LINE__);
364 365 366 367 368 369 370 371
        return NULL;
    }

    self->atlas = atlas;
    self->size  = pt_size;

    self->location = TEXTURE_FONT_FILE;
    self->filename = strdup(filename);
Bernd Paysan's avatar
Bernd Paysan committed
372 373
    self->mode = mode_default;
    
374 375 376 377 378
    if (texture_font_init(self)) {
        texture_font_delete(self);
        return NULL;
    }

379
    return self;
Nicolas.Rougier's avatar
Nicolas.Rougier committed
380 381
}

382 383 384
// ------------------------------------------- texture_font_new_from_memory ---
texture_font_t *
texture_font_new_from_memory(texture_atlas_t *atlas, float pt_size,
385
			     const void *memory_base, size_t memory_size)
386 387 388 389 390 391 392 393
{
    texture_font_t *self;

    assert(memory_base);
    assert(memory_size);

    self = calloc(1, sizeof(*self));
    if (!self) {
394
        freetype_gl_error( Out_Of_Memory,
395
			   "line %d: No more memory for allocating data\n", __LINE__);
396 397 398 399 400 401 402 403 404
        return NULL;
    }

    self->atlas = atlas;
    self->size  = pt_size;

    self->location = TEXTURE_FONT_MEMORY;
    self->memory.base = memory_base;
    self->memory.size = memory_size;
Bernd Paysan's avatar
Bernd Paysan committed
405 406
    self->mode = mode_default;
    
407 408 409 410 411 412 413
    if (texture_font_init(self)) {
        texture_font_delete(self);
        return NULL;
    }

    return self;
}
Nicolas.Rougier's avatar
Nicolas.Rougier committed
414

Bernd Paysan's avatar
Bernd Paysan committed
415 416 417 418 419
// ----------------------------------------------------- texture_font_clone ---
texture_font_t *
texture_font_clone( texture_font_t *old, float pt_size)
{
    texture_font_t *self;
420
    FT_Error error = 0;
421
    float native_size = old->size / old->scale; // unscale fonts
422
    
Bernd Paysan's avatar
Bernd Paysan committed
423 424 425 426 427 428 429 430
    self = calloc(1, sizeof(*self));
    if (!self) {
        freetype_gl_error( Out_Of_Memory,
			   "line %d: No more memory for allocating data\n", __LINE__);
        return NULL;
    }

    memcpy(self, old, sizeof(*self));
431
    self->size  = pt_size;
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446

    error = FT_New_Size( self->face, &self->ft_size );
    if(error) {
	freetype_error( error, "FT_Error (%s:%d, code 0x%02x) : %s\n",
			__FILENAME__, __LINE__, FT_Errors[error].code, FT_Errors[error].message);
	return NULL;
    }

    error = FT_Activate_Size( self->ft_size );
    if(error) {
	freetype_error( error, "FT_Error (%s:%d, code 0x%02x) : %s\n",
			__FILENAME__, __LINE__, FT_Errors[error].code, FT_Errors[error].message);
	return NULL;
    }
    
Bernd Paysan's avatar
Bernd Paysan committed
447 448 449 450 451 452 453 454
    if(!texture_font_set_size ( self, pt_size * 100.f ))
	return NULL;

    texture_font_init_size( self );
    
    if(!texture_font_set_size ( self, pt_size ))
	return NULL;

455 456
    if(self->size / self->scale != native_size)
	self->glyphs = vector_new(sizeof(texture_glyph_t *));
Bernd Paysan's avatar
Bernd Paysan committed
457 458
    return self;
}
459 460 461 462 463 464 465 466
// ----------------------------------------------------- texture_font_close ---

void
texture_font_close( texture_font_t *self, font_mode_t face_mode, font_mode_t library_mode )
{
    if( self->face && self->mode <= face_mode ) {
	FT_Done_Face( self->face );
	self->face = NULL;
Bernd Paysan's avatar
Bernd Paysan committed
467 468
    } else {
	return; // never close the library when the face stays open
469 470
    }

Bernd Paysan's avatar
Bernd Paysan committed
471 472 473
    if( self->library && self->library->library && self->library->mode <= library_mode ) {
	FT_Done_FreeType( self->library->library );
	self->library->library = NULL;
474 475 476 477 478 479
    }
}

// ------------------------------------------------- texture_font_load_face ---

int
Bernd Paysan's avatar
Bernd Paysan committed
480
texture_font_load_face( texture_font_t *self, float size )
481
{
Bernd Paysan's avatar
Bernd Paysan committed
482 483 484 485 486 487 488
    FT_Error error;

    if ( !self->library ) {
	if ( !freetype_gl_library ) {
	    freetype_gl_library = texture_library_new();
	}
	self->library = freetype_gl_library;
489
    }
Bernd Paysan's avatar
Bernd Paysan committed
490 491 492
    
    if( !self->library->library ) {
	error = FT_Init_FreeType( &self->library->library );
493 494 495
	if(error) {
	    freetype_error(error, "FT_Error (0x%02x) : %s\n",
			   FT_Errors[error].code, FT_Errors[error].message);
496
	    goto cleanup;
497 498
	}
    }
Bernd Paysan's avatar
Bernd Paysan committed
499
    
500 501 502 503
    if( !self->face ) {
	switch (self->location) {
	case TEXTURE_FONT_FILE:
	    error = FT_New_Face(self->library->library, self->filename, 0, &self->face);
Bernd Paysan's avatar
Bernd Paysan committed
504 505 506 507 508
	    if(error) {
		freetype_error( error, "FT_Error, file %s (%s:%d, code 0x%02x) : %s\n",
				self->filename, __FILENAME__, __LINE__, FT_Errors[error].code, FT_Errors[error].message);
		goto cleanup_library;
	    }
509 510 511 512 513
	    break;

	case TEXTURE_FONT_MEMORY:
	    error = FT_New_Memory_Face(self->library->library,
				       self->memory.base, self->memory.size, 0, &self->face);
Bernd Paysan's avatar
Bernd Paysan committed
514 515 516 517 518 519
	    if(error) {
		freetype_error( error, "FT_Error memory %p:%x (%s:%d, code 0x%02x) : %s\n",
				self->memory.base, self->memory.size,
				__FILENAME__, __LINE__, FT_Errors[error].code, FT_Errors[error].message);
		goto cleanup_library;
	    }
520 521 522
	    break;
	}

523 524 525 526 527 528 529
	/* Select charmap */
	error = FT_Select_Charmap(self->face, FT_ENCODING_UNICODE);
	if(error) {
	    freetype_error( error, "FT_Error (%s:%d, code 0x%02x) : %s\n",
			    __FILENAME__, __LINE__, FT_Errors[error].code, FT_Errors[error].message);
	    goto cleanup_face;
	}
Bernd Paysan's avatar
Bernd Paysan committed
530

531 532 533 534 535 536 537 538 539 540 541 542 543 544
	error = FT_New_Size( self->face, &self->ft_size );
	if(error) {
	    freetype_error( error, "FT_Error (%s:%d, code 0x%02x) : %s\n",
			    __FILENAME__, __LINE__, FT_Errors[error].code, FT_Errors[error].message);
	    goto cleanup_face;
	}

	error = FT_Activate_Size( self->ft_size );
	if(error) {
	    freetype_error( error, "FT_Error (%s:%d, code 0x%02x) : %s\n",
			    __FILENAME__, __LINE__, FT_Errors[error].code, FT_Errors[error].message);
	    goto cleanup_face;
	}
	
Bernd Paysan's avatar
Bernd Paysan committed
545
	if(!texture_font_set_size ( self, size ))
Bernd Paysan's avatar
Bernd Paysan committed
546
	    goto cleanup_face;
547
    }
Bernd Paysan's avatar
Bernd Paysan committed
548
    
549
    return 1;
Bernd Paysan's avatar
Bernd Paysan committed
550
    
551
  cleanup_face:
Bernd Paysan's avatar
Bernd Paysan committed
552 553
    texture_font_close( self, MODE_ALWAYS_OPEN, MODE_FREE_CLOSE );
    return 0;
554 555 556 557 558 559
  cleanup_library:
    texture_font_close( self, MODE_ALWAYS_OPEN, MODE_ALWAYS_OPEN );
  cleanup:
    return 0;
}

560 561 562 563
// ---------------------------------------------------- texture_font_delete ---
void
texture_font_delete( texture_font_t *self )
{
564
    size_t i;
565
    texture_glyph_t *glyph;
566
    FT_Error error=0;
567

568
    assert( self );
Nicolas.Rougier's avatar
Nicolas.Rougier committed
569

570 571 572 573 574 575
    error = FT_Done_Size( self->ft_size );
    if(error) {
	freetype_error( error, "FT_Error (%s:%d, code 0x%02x) : %s\n",
			__FILENAME__, __LINE__, FT_Errors[error].code, FT_Errors[error].message);
    }

576 577
    texture_font_close( self, MODE_ALWAYS_OPEN, MODE_FREE_CLOSE );

578
    if(self->location == TEXTURE_FONT_FILE && self->filename)
579 580
	free( self->filename );
	
581
    GLYPHS_ITERATOR(i, glyph, self->glyphs) {
582
	texture_glyph_delete( glyph );
583
    } GLYPHS_ITERATOR_END1
Bernd Paysan's avatar
Bernd Paysan committed
584
	free( __glyphs );
585 586
    GLYPHS_ITERATOR_END2;

587 588 589 590
    vector_delete( self->glyphs );
    free( self );
}

Bernd Paysan's avatar
Bernd Paysan committed
591
// ------------------------------------------------ texture_font_find_glyph ---
592 593
texture_glyph_t *
texture_font_find_glyph( texture_font_t * self,
594
                         const char * codepoint )
595
{
Bernd Paysan's avatar
Bernd Paysan committed
596
    if(!codepoint)
Bernd Paysan's avatar
Bernd Paysan committed
597
	return (texture_glyph_t *)self->atlas->special;
Bernd Paysan's avatar
Bernd Paysan committed
598 599 600 601 602 603 604 605 606 607 608 609
    
    return texture_font_find_glyph_gi(self, utf8_to_utf32( codepoint ));
}

// ---------------------------------------------- texture_font_find_glyph_gi ---
texture_glyph_t *
texture_font_find_glyph_gi( texture_font_t * self,
			    uint32_t codepoint )
{
    uint32_t i = codepoint >> 8;
    uint32_t j = codepoint & 0xFF;
    texture_glyph_t **glyph_index1, *glyph;
Bernd Paysan's avatar
Bernd Paysan committed
610

Bernd Paysan's avatar
Bernd Paysan committed
611
    if(self->glyphs->size <= i)
Bernd Paysan's avatar
Bernd Paysan committed
612 613 614 615
	return NULL;

    glyph_index1 = *(texture_glyph_t ***) vector_get( self->glyphs, i );

Bernd Paysan's avatar
Bernd Paysan committed
616
    if(!glyph_index1)
Bernd Paysan's avatar
Bernd Paysan committed
617
	return NULL;
Bernd Paysan's avatar
Bernd Paysan committed
618
    else
619 620 621
	glyph = glyph_index1[j];

    while( glyph && // if no glyph is there, we are done here
622 623
	   (glyph->rendermode != self->rendermode ||
	    glyph->outline_thickness != self->outline_thickness) ) {
624 625 626 627 628
	if( glyph->glyphmode != GLYPH_CONT)
	    return NULL;
	glyph++;
    }
    return glyph;
Bernd Paysan's avatar
Bernd Paysan committed
629 630
}

631 632 633 634
int
texture_font_index_glyph( texture_font_t * self,
			  texture_glyph_t *glyph,
			  uint32_t codepoint)
Bernd Paysan's avatar
Bernd Paysan committed
635
{
636 637
    uint32_t i = codepoint >> 8;
    uint32_t j = codepoint & 0xFF;
638
    texture_glyph_t ***glyph_index1, *glyph_insert;
Bernd Paysan's avatar
Bernd Paysan committed
639 640 641

    if(self->glyphs->size <= i) {
	vector_resize( self->glyphs, i+1);
642 643
    }

Bernd Paysan's avatar
Bernd Paysan committed
644 645 646 647 648 649
    glyph_index1 = (texture_glyph_t ***) vector_get( self->glyphs, i );

    if(!*glyph_index1) {
	*glyph_index1 = calloc( 0x100, sizeof(texture_glyph_t*) );
    }

650 651 652 653 654 655 656 657 658 659 660 661 662 663
    if(( glyph_insert = (*glyph_index1)[j] )) {
	int i = 0;
	// fprintf(stderr, "glyph already there\n");
	while (glyph_insert[i].glyphmode != GLYPH_END)
	    i++;
	// fprintf(stderr, "Insert a glyph after position %d\n", i);
	glyph_insert[i].glyphmode = GLYPH_CONT;
	(*glyph_index1)[j] = glyph_insert = realloc( glyph_insert, sizeof(texture_glyph_t)*(i+2) );
	memcpy( glyph_insert+(i+1), glyph, sizeof(texture_glyph_t) );
	return 1;
    } else {
	(*glyph_index1)[j] = glyph;
	return 0;
    }
664
}
665

666 667 668
// ------------------------------------------------ texture_font_load_glyph ---
int
texture_font_load_glyph( texture_font_t * self,
Bernd Paysan's avatar
Bernd Paysan committed
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
			 const char * codepoint )
{
    /* codepoint NULL is special : it is used for line drawing (overline,
     * underline, strikethrough) and background.
     */
    if( !codepoint ) {
	return 1;
    }
    uint32_t ucodepoint = utf8_to_utf32(codepoint);

    return texture_font_load_glyph_gi( self,
				       FT_Get_Char_Index( self->face, ucodepoint),
				       ucodepoint);
}

// ------------------------------------------------ texture_font_load_glyph ---
int
texture_font_load_glyph_gi( texture_font_t * self,
			    uint32_t glyph_index,
			    uint32_t ucodepoint )
Nicolas.Rougier's avatar
Nicolas.Rougier committed
689
{
690
    size_t i, x, y;
691

692 693
    FT_Error error;
    FT_Face face;
694 695 696 697
    FT_Glyph ft_glyph;
    FT_GlyphSlot slot;
    FT_Bitmap ft_bitmap;

698
    texture_glyph_t *glyph;
699 700 701 702
    FT_Int32 flags = 0;
    int ft_glyph_top = 0;
    int ft_glyph_left = 0;

703 704
    ivec4 region;
    size_t missed = 0;
705

706
    /* Check if codepoint has been already loaded */
Bernd Paysan's avatar
Bernd Paysan committed
707
    if (texture_font_find_glyph_gi(self, ucodepoint)) {
708
        return 1;
Raphael Taylor-Davies's avatar
Raphael Taylor-Davies committed
709
    }
710

Bernd Paysan's avatar
Bernd Paysan committed
711
    if (!texture_font_load_face(self, self->size))
Bernd Paysan's avatar
Bernd Paysan committed
712 713
        return 0;

714 715 716
    flags = 0;
    ft_glyph_top = 0;
    ft_glyph_left = 0;
Bernd Paysan's avatar
Bernd Paysan committed
717
    if(!glyph_index) {
718
	texture_glyph_t * glyph;
719
	if ((glyph = texture_font_find_glyph(self, "\0"))) {
720
	    texture_font_index_glyph( self, glyph, ucodepoint );
Bernd Paysan's avatar
Bernd Paysan committed
721
	    texture_font_close( self, MODE_AUTO_CLOSE, MODE_AUTO_CLOSE );
Bernd Paysan's avatar
Bernd Paysan committed
722 723 724
	    return 1;
	}
    }
725 726 727
    // WARNING: We use texture-atlas depth to guess if user wants
    //          LCD subpixel rendering

728
    if( self->rendermode != RENDER_NORMAL && self->rendermode != RENDER_SIGNED_DISTANCE_FIELD )
729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
    {
        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;
    }

746
    if( self->atlas->depth == 3 )
747
    {
Bernd Paysan's avatar
Bernd Paysan committed
748
        FT_Library_SetLcdFilter( self->library->library, FT_LCD_FILTER_LIGHT );
749 750 751
        flags |= FT_LOAD_TARGET_LCD;

        if( self->filtering )
752
        {
Bernd Paysan's avatar
Bernd Paysan committed
753
            FT_Library_SetLcdFilterWeights( self->library->library, self->lcd_weights );
754
        }
755 756
    }

757 758
    if( self->atlas->depth == 4 )
    {
759
#ifdef FT_LOAD_COLOR
760
	flags |= FT_LOAD_COLOR;
761
#else
Bernd Paysan's avatar
Bernd Paysan committed
762 763
	freetype_error( Load_Color_Not_Available, "FT_Error (%s:%d, code 0x%02x) : %s\n",
			__FILENAME__, __LINE__, 0, "FT_LOAD_COLOR not available");
764
#endif
765 766
    }

767 768 769 770 771 772 773
    error = FT_Activate_Size( self->ft_size );
    if(error) {
	freetype_error( error, "FT_Error (%s:%d, code 0x%02x) : %s\n",
			__FILENAME__, __LINE__, FT_Errors[error].code, FT_Errors[error].message);
	return 0;
    }

Bernd Paysan's avatar
Bernd Paysan committed
774
    error = FT_Load_Glyph( self->face, glyph_index, flags );
775 776
    if( error )
    {
Bernd Paysan's avatar
Bernd Paysan committed
777
        freetype_error( error, "FT_Error (%s:%d, code 0x%02x) : %s\n",
Bernd Paysan's avatar
Bernd Paysan committed
778 779
			__FILENAME__, __LINE__, FT_Errors[error].code, FT_Errors[error].message);
	texture_font_close( self, MODE_AUTO_CLOSE, MODE_AUTO_CLOSE );
780 781
        return 0;
    }
782

783
    if( self->rendermode == RENDER_NORMAL || self->rendermode == RENDER_SIGNED_DISTANCE_FIELD )
784
    {
Bernd Paysan's avatar
Bernd Paysan committed
785
        slot            = self->face->glyph;
786 787 788 789 790 791 792 793
        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;
794

Bernd Paysan's avatar
Bernd Paysan committed
795
        error = FT_Stroker_New( self->library->library, &stroker );
796

797
        if( error )
798
        {
799
            freetype_error( error, "FT_Error (0x%02x) : %s\n",
800
			    FT_Errors[error].code, FT_Errors[error].message);
801
            goto cleanup_stroker;
802
        }
803

804 805 806 807 808
        FT_Stroker_Set(stroker,
                        (int)(self->outline_thickness * HRES),
                        FT_STROKER_LINECAP_ROUND,
                        FT_STROKER_LINEJOIN_ROUND,
                        0);
809

Bernd Paysan's avatar
Bernd Paysan committed
810
        error = FT_Get_Glyph( self->face->glyph, &ft_glyph);
811

812
        if( error )
813
        {
814
            freetype_error( error, "FT_Error (0x%02x) : %s\n",
815
			    FT_Errors[error].code, FT_Errors[error].message);
816
            goto cleanup_stroker;
817 818
        }

819 820 821 822 823 824
        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 );
825

Nicolas.Rougier's avatar
Nicolas.Rougier committed
826 827
        if( error )
        {
828
            freetype_error( error, "FT_Error (0x%02x) : %s\n",
829
			    FT_Errors[error].code, FT_Errors[error].message);
830
            goto cleanup_stroker;
Nicolas.Rougier's avatar
Nicolas.Rougier committed
831
        }
832

833 834 835 836 837 838 839 840 841 842 843
	switch( self->atlas->depth ) {
	case 1:
	    error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
	    break;
	case 3:
	    error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_LCD, 0, 1);
	    break;
	case 4:
	    error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
	    break;
	}
844 845 846

        if( error )
        {
847
            freetype_error( error, "FT_Error (0x%02x) : %s\n",
848 849
                    FT_Errors[error].code, FT_Errors[error].message);
            goto cleanup_stroker;
Nicolas.Rougier's avatar
Nicolas.Rougier committed
850
        }
851

852 853 854 855
        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;
856 857 858 859 860 861

cleanup_stroker:
        FT_Stroker_Done( stroker );

        if( error )
        {
Bernd Paysan's avatar
Bernd Paysan committed
862
	    texture_font_close( self, MODE_AUTO_CLOSE, MODE_AUTO_CLOSE );
863 864
            return 0;
        }
865
    }
866

867 868 869 870 871 872 873 874 875 876 877 878 879
    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;
    }

Bernd Paysan's avatar
Bernd Paysan committed
880
    size_t src_w = self->atlas->depth == 3 ? ft_bitmap.width/3 : ft_bitmap.width;
881 882 883 884 885 886 887
    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 );

888 889
    if ( region.x < 0 )
    {
890 891 892 893
        freetype_gl_warning( Texture_Atlas_Full,
			     "Texture atlas is full, asked for %i*%i (%s:%d)\n",
			     tgt_w, tgt_h,
			     __FILENAME__, __LINE__ );
Bernd Paysan's avatar
Bernd Paysan committed
894
	texture_font_close( self, MODE_AUTO_CLOSE, MODE_AUTO_CLOSE );
895 896
        return 0;
    }
897

898 899
    x = region.x;
    y = region.y;
900

Bernd Paysan's avatar
Bernd Paysan committed
901
    unsigned char *buffer = calloc( tgt_w * tgt_h * self->atlas->depth, sizeof(unsigned char) );
902

903 904
    unsigned char *dst_ptr = buffer + (padding.top * tgt_w + padding.left) * self->atlas->depth;
    unsigned char *src_ptr = ft_bitmap.buffer;
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928
    if( self->atlas->depth == 4 ) {
	for( i = 0; i < src_h; i++ ) {
	    int j;
	    // flip bgra to rgba, because that's better for OpenGL
	    for( j = 0; j < ft_bitmap.width; j++ ) {
		uint32_t bgra, rgba;
		bgra = ((uint32_t*)src_ptr)[j];
#if __BYTE_ORDER == __BIG_ENDIAN
		rgba = rol8(__builtin_bswap32(bgra));
#else
		rgba = ror8(__builtin_bswap32(bgra));
#endif
		((uint32_t*)dst_ptr)[j] = rgba;
	    }
	    dst_ptr += tgt_w * self->atlas->depth;
	    src_ptr += ft_bitmap.pitch;
	}
    } else {
	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
	    memcpy( dst_ptr, src_ptr, ft_bitmap.width);
	    dst_ptr += tgt_w * self->atlas->depth;
	    src_ptr += ft_bitmap.pitch;
	}
929 930
    }

931 932 933 934 935
    if( self->rendermode == RENDER_SIGNED_DISTANCE_FIELD )
    {
        unsigned char *sdf = make_distance_mapb( buffer, tgt_w, tgt_h );
        free( buffer );
        buffer = sdf;
936 937
    }

938
    texture_atlas_set_region( self->atlas, x, y, tgt_w, tgt_h, buffer, tgt_w * self->atlas->depth);
939 940

    free( buffer );
941 942

    glyph = texture_glyph_new( );
Bernd Paysan's avatar
Bernd Paysan committed
943
    glyph->codepoint = glyph_index ? ucodepoint : 0;
944
;
945 946
    glyph->width    = tgt_w;
    glyph->height   = tgt_h;
947 948
    glyph->rendermode = self->rendermode;
    glyph->outline_thickness = self->outline_thickness;
949 950
    glyph->offset_x = ft_glyph_left;
    glyph->offset_y = ft_glyph_top;
951 952 953 954 955 956 957 958 959 960 961 962
    if(self->scaletex) {
	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;
    } else {
	// fix up unscaled coordinates by subtracting 0.5
	// this avoids drawing pixels from neighboring characters
	// note that you also have to paint these glyphs with an offset of
	// half a pixel each to get crisp rendering
	glyph->s0       = x - 0.5;
	glyph->t0       = y - 0.5;
963 964
	glyph->s1       = x + tgt_w - 0.5;
	glyph->t1       = y + tgt_h - 0.5;
965
    }
Bernd Paysan's avatar
Bernd Paysan committed
966
    slot = self->face->glyph;
967 968
    if( self->atlas->depth == 4 ) {
	// color fonts use actual pixels, not subpixels
969 970
	glyph->advance_x = slot->advance.x;
	glyph->advance_y = slot->advance.y;
971
    } else {
972 973
	glyph->advance_x = slot->advance.x * self->scale / HRESf;
	glyph->advance_y = slot->advance.y * self->scale / HRESf;
974
    }
975

976
    int free_glyph = texture_font_index_glyph(self, glyph, ucodepoint);
977
    if(!glyph_index) {
978 979 980 981 982 983
	if(!free_glyph) {
	    texture_glyph_t *new_glyph = malloc(sizeof(texture_glyph_t));
	    memcpy(new_glyph, glyph, sizeof(texture_glyph_t));
	    glyph=new_glyph;
	}
	free_glyph = texture_font_index_glyph(self, glyph, 0);
984 985 986 987
    }
    if(free_glyph) {
	// fprintf(stderr, "Free glyph\n");
	free(glyph);
988 989
    }
    
990
    if( self->rendermode != RENDER_NORMAL && self->rendermode != RENDER_SIGNED_DISTANCE_FIELD )
991
        FT_Done_Glyph( ft_glyph );
992

Bernd Paysan's avatar
Bernd Paysan committed
993
    texture_font_generate_kerning( self, &self->library->library, &self->face );
994

Bernd Paysan's avatar
Bernd Paysan committed
995
    texture_font_close( self, MODE_AUTO_CLOSE, MODE_AUTO_CLOSE );
996

997 998 999 1000 1001 1002 1003 1004 1005 1006
    return 1;
}

// ----------------------------------------------- texture_font_load_glyphs ---
size_t
texture_font_load_glyphs( texture_font_t * self,
                          const char * codepoints )
{
    size_t i;

Bernd Paysan's avatar
Bernd Paysan committed
1007 1008
    self->mode++;

1009
    /* Load each glyph */
Bernd Paysan's avatar
Bernd Paysan committed
1010
    for( i = 0; i < strlen(codepoints); i += utf8_surrogate_len(codepoints + i) ) {
Bernd Paysan's avatar
Bernd Paysan committed
1011 1012 1013
        if( !texture_font_load_glyph( self, codepoints + i ) ) {
	    self->mode--;
	    texture_font_close( self, MODE_AUTO_CLOSE, MODE_AUTO_CLOSE );
1014

Bernd Paysan's avatar
Bernd Paysan committed
1015 1016
            return utf8_strlen( codepoints + i );
	}
1017 1018
    }

Bernd Paysan's avatar
Bernd Paysan committed
1019 1020 1021
    self->mode--;
    texture_font_close( self, MODE_AUTO_CLOSE, MODE_AUTO_CLOSE );

1022
    return 0;
Nicolas.Rougier's avatar
Nicolas.Rougier committed
1023 1024 1025
}


1026 1027 1028
// ------------------------------------------------- texture_font_get_glyph ---
texture_glyph_t *
texture_font_get_glyph( texture_font_t * self,
1029
                        const char * codepoint )
Nicolas.Rougier's avatar
Nicolas.Rougier committed
1030
{
1031
    texture_glyph_t *glyph;
Nicolas.Rougier's avatar
Nicolas.Rougier committed
1032 1033 1034 1035 1036

    assert( self );
    assert( self->filename );
    assert( self->atlas );

1037
    /* Check if codepoint has been already loaded */
Bernd Paysan's avatar
Bernd Paysan committed
1038
    if( !(glyph = texture_font_find_glyph( self, codepoint )) )
1039
    /* Glyph has not been already loaded */
Bernd Paysan's avatar
Bernd Paysan committed
1040 1041
	if( texture_font_load_glyph( self, codepoint ) )
	    glyph = texture_font_find_glyph( self, codepoint );
1042

Bernd Paysan's avatar
Bernd Paysan committed
1043
    return glyph;
Nicolas.Rougier's avatar
Nicolas.Rougier committed
1044
}
1045

Bernd Paysan's avatar
Bernd Paysan committed
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067
// ----------------------------------------------- texture_font_get_glyph_gi ---
texture_glyph_t *
texture_font_get_glyph_gi( texture_font_t * self,
			   uint32_t glyph_index )
{
    texture_glyph_t *glyph;

    assert( self );
    assert( self->filename );
    assert( self->atlas );

    /* Check if glyph_index has been already loaded */
    if( (glyph = texture_font_find_glyph_gi( self, glyph_index )) )
        return glyph;

    /* Glyph has not been already loaded */
    if( texture_font_load_glyph_gi( self, glyph_index, glyph_index ) )
        return texture_font_find_glyph_gi( self, glyph_index );

    return NULL;
}

1068
// ------------------------------------------  texture_font_enlarge_texture ---
chris-b-h's avatar
chris-b-h committed
1069
void
1070 1071
texture_font_enlarge_texture( texture_font_t * self, size_t width_new,
			      size_t height_new)
chris-b-h's avatar
chris-b-h committed
1072 1073
{
    assert(self);
Bernd Paysan's avatar
Bernd Paysan committed
1074 1075

    texture_atlas_enlarge_texture ( self->atlas, width_new, height_new);
1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100
}
// -------------------------------------------- texture_font_enlarge_atlas ---
void
texture_font_enlarge_glyphs( texture_font_t * self, float mulw, float mulh)
{
    size_t i;
    texture_glyph_t* g;
    GLYPHS_ITERATOR(i, g, self->glyphs) {
	g->s0 *= mulw;
	g->s1 *= mulw;
	g->t0 *= mulh;
	g->t1 *= mulh;
    } GLYPHS_ITERATOR_END
}

// -------------------------------------------  texture_font_enlarge_atlas ---
void
texture_font_enlarge_atlas( texture_font_t * self, size_t width_new,
			    size_t height_new)
{
    texture_atlas_t* ta = self->atlas;
    size_t width_old = ta->width;
    size_t height_old = ta->height;    

    texture_font_enlarge_texture( self, width_new, height_new);
1101 1102 1103 1104 1105
    if( self->scaletex ) {
	float mulw = (float)width_old / width_new;
	float mulh = (float)height_old / height_new;
	texture_font_enlarge_glyphs( self, mulw, mulh );
    }
chris-b-h's avatar
chris-b-h committed
1106
}