comparison src/texture-font.c @ 0:785057719d9b

Import.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 05 Aug 2013 12:25:43 +0300
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:785057719d9b
1 /* ===========================================================================
2 * Freetype GL - A C OpenGL Freetype engine
3 * Platform: Any
4 * WWW: http://code.google.com/p/freetype-gl/
5 * ----------------------------------------------------------------------------
6 * Copyright 2011,2012 Nicolas P. Rougier. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY NICOLAS P. ROUGIER ''AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
21 * EVENT SHALL NICOLAS P. ROUGIER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * The views and conclusions contained in the software and documentation are
30 * those of the authors and should not be interpreted as representing official
31 * policies, either expressed or implied, of Nicolas P. Rougier.
32 * ============================================================================
33 */
34 #include <ft2build.h>
35 #include FT_FREETYPE_H
36 #include FT_STROKER_H
37 // #include FT_ADVANCES_H
38 #include FT_LCD_FILTER_H
39 #include <stdint.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <assert.h>
43 #include <math.h>
44 #include <wchar.h>
45 #include "platform.h"
46 #include "texture-font.h"
47
48 #undef __FTERRORS_H__
49 #define FT_ERRORDEF( e, v, s ) { e, s },
50 #define FT_ERROR_START_LIST {
51 #define FT_ERROR_END_LIST { 0, 0 } };
52 const struct {
53 int code;
54 const char* message;
55 } FT_Errors[] =
56 #include FT_ERRORS_H
57
58
59
60
61 // ------------------------------------------------- texture_font_load_face ---
62 int
63 texture_font_load_face( FT_Library * library,
64 const char * filename,
65 const float size,
66 FT_Face * face )
67 {
68 size_t hres = 64;
69 FT_Error error;
70 FT_Matrix matrix = { (int)((1.0/hres) * 0x10000L),
71 (int)((0.0) * 0x10000L),
72 (int)((0.0) * 0x10000L),
73 (int)((1.0) * 0x10000L) };
74
75 assert( library );
76 assert( filename );
77 assert( size );
78
79 /* Initialize library */
80 error = FT_Init_FreeType( library );
81 if( error )
82 {
83 fprintf(stderr, "FT_Error (0x%02x) : %s\n",
84 FT_Errors[error].code, FT_Errors[error].message);
85 return 0;
86 }
87
88 /* Load face */
89 error = FT_New_Face( *library, filename, 0, face );
90 if( error )
91 {
92 fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n",
93 __LINE__, FT_Errors[error].code, FT_Errors[error].message);
94 FT_Done_FreeType( *library );
95 return 0;
96 }
97
98 /* Select charmap */
99 error = FT_Select_Charmap( *face, FT_ENCODING_UNICODE );
100 if( error )
101 {
102 fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n",
103 __LINE__, FT_Errors[error].code, FT_Errors[error].message );
104 FT_Done_Face( *face );
105 FT_Done_FreeType( *library );
106 return 0;
107 }
108
109 /* Set char size */
110 error = FT_Set_Char_Size( *face, (int)(size*64), 0, 72*hres, 72 );
111 if( error )
112 {
113 fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n",
114 __LINE__, FT_Errors[error].code, FT_Errors[error].message );
115 FT_Done_Face( *face );
116 FT_Done_FreeType( *library );
117 return 0;
118 }
119
120 /* Set transform matrix */
121 FT_Set_Transform( *face, &matrix, NULL );
122
123 return 1;
124 }
125
126
127 // ------------------------------------------------------ texture_glyph_new ---
128 texture_glyph_t *
129 texture_glyph_new( void )
130 {
131 texture_glyph_t *self = (texture_glyph_t *) malloc( sizeof(texture_glyph_t) );
132 if( self == NULL)
133 {
134 fprintf( stderr,
135 "line %d: No more memory for allocating data\n", __LINE__ );
136 exit( EXIT_FAILURE );
137 }
138 self->id = 0;
139 self->width = 0;
140 self->height = 0;
141 self->outline_type = 0;
142 self->outline_thickness = 0.0;
143 self->offset_x = 0;
144 self->offset_y = 0;
145 self->advance_x = 0.0;
146 self->advance_y = 0.0;
147 self->s0 = 0.0;
148 self->t0 = 0.0;
149 self->s1 = 0.0;
150 self->t1 = 0.0;
151 self->kerning = vector_new( sizeof(kerning_t) );
152 return self;
153 }
154
155
156 // --------------------------------------------------- texture_glyph_delete ---
157 void
158 texture_glyph_delete( texture_glyph_t *self )
159 {
160 assert( self );
161 vector_delete( self->kerning );
162 free( self );
163 }
164
165 // ---------------------------------------------- texture_glyph_get_kerning ---
166 float
167 texture_glyph_get_kerning( const texture_glyph_t * self,
168 const wchar_t charcode )
169 {
170 size_t i;
171
172 assert( self );
173 for( i=0; i<vector_size(self->kerning); ++i )
174 {
175 kerning_t * kerning = (kerning_t *) vector_get( self->kerning, i );
176 if( kerning->charcode == charcode )
177 {
178 return kerning->kerning;
179 }
180 }
181 return 0;
182 }
183
184
185 // ------------------------------------------ texture_font_generate_kerning ---
186 void
187 texture_font_generate_kerning( texture_font_t *self )
188 {
189 size_t i, j;
190 FT_Library library;
191 FT_Face face;
192 FT_UInt glyph_index, prev_index;
193 texture_glyph_t *glyph, *prev_glyph;
194 FT_Vector kerning;
195
196 assert( self );
197
198 /* Load font */
199 if( !texture_font_load_face( &library, self->filename, self->size, &face ) )
200 {
201 return;
202 }
203
204 /* For each glyph couple combination, check if kerning is necessary */
205 /* Starts at index 1 since 0 is for the special backgroudn glyph */
206 for( i=1; i<self->glyphs->size; ++i )
207 {
208 glyph = *(texture_glyph_t **) vector_get( self->glyphs, i );
209 glyph_index = FT_Get_Char_Index( face, glyph->charcode );
210 vector_clear( glyph->kerning );
211
212 for( j=1; j<self->glyphs->size; ++j )
213 {
214 prev_glyph = *(texture_glyph_t **) vector_get( self->glyphs, j );
215 prev_index = FT_Get_Char_Index( face, prev_glyph->charcode );
216 FT_Get_Kerning( face, prev_index, glyph_index, FT_KERNING_UNFITTED, &kerning );
217 // printf("%c(%d)-%c(%d): %ld\n",
218 // prev_glyph->charcode, prev_glyph->charcode,
219 // glyph_index, glyph_index, kerning.x);
220 if( kerning.x )
221 {
222 // 64 * 64 because of 26.6 encoding AND the transform matrix used
223 // in texture_font_load_face (hres = 64)
224 kerning_t k = {prev_glyph->charcode, kerning.x / (float)(64.0f*64.0f)};
225 vector_push_back( glyph->kerning, &k );
226 }
227 }
228 }
229 FT_Done_Face( face );
230 FT_Done_FreeType( library );
231 }
232
233
234 // ------------------------------------------------------- texture_font_new ---
235 texture_font_t *
236 texture_font_new( texture_atlas_t * atlas,
237 const char * filename,
238 const float size)
239 {
240 texture_font_t *self = (texture_font_t *) malloc( sizeof(texture_font_t) );
241 FT_Library library;
242 FT_Face face;
243 FT_Size_Metrics metrics;
244
245 assert( filename );
246 assert( size );
247
248 if( self == NULL)
249 {
250 fprintf( stderr,
251 "line %d: No more memory for allocating data\n", __LINE__ );
252 exit( EXIT_FAILURE );
253 }
254 self->glyphs = vector_new( sizeof(texture_glyph_t *) );
255 self->atlas = atlas;
256 self->height = 0;
257 self->ascender = 0;
258 self->descender = 0;
259 self->filename = strdup( filename );
260 self->size = size;
261 self->outline_type = 0;
262 self->outline_thickness = 0.0;
263 self->hinting = 1;
264 self->kerning = 1;
265 self->filtering = 1;
266 // FT_LCD_FILTER_LIGHT is (0x00, 0x55, 0x56, 0x55, 0x00)
267 // FT_LCD_FILTER_DEFAULT is (0x10, 0x40, 0x70, 0x40, 0x10)
268 self->lcd_weights[0] = 0x10;
269 self->lcd_weights[1] = 0x40;
270 self->lcd_weights[2] = 0x70;
271 self->lcd_weights[3] = 0x40;
272 self->lcd_weights[4] = 0x10;
273
274 /* Get font metrics at high resolution */
275
276 if( !texture_font_load_face( &library, self->filename, self->size*100, &face ) )
277 {
278 return self;
279 }
280
281 // 64 * 64 because of 26.6 encoding AND the transform matrix used
282 // in texture_font_load_face (hres = 64)
283 self->underline_position = face->underline_position / (float)(64.0f*64.0f) * self->size;
284 self->underline_position = round( self->underline_position );
285 if( self->underline_position > -2 )
286 {
287 self->underline_position = -2.0;
288 }
289
290 self->underline_thickness = face->underline_thickness / (float)(64.0f*64.0f) * self->size;
291 self->underline_thickness = round( self->underline_thickness );
292 if( self->underline_thickness < 1 )
293 {
294 self->underline_thickness = 1.0;
295 }
296
297 metrics = face->size->metrics;
298 self->ascender = (metrics.ascender >> 6) / 100.0;
299 self->descender = (metrics.descender >> 6) / 100.0;
300 self->height = (metrics.height >> 6) / 100.0;
301 self->linegap = self->height - self->ascender + self->descender;
302 FT_Done_Face( face );
303 FT_Done_FreeType( library );
304
305 /* -1 is a special glyph */
306 texture_font_get_glyph( self, -1 );
307
308 return self;
309 }
310
311
312 // ---------------------------------------------------- texture_font_delete ---
313 void
314 texture_font_delete( texture_font_t *self )
315 {
316 size_t i;
317 texture_glyph_t *glyph;
318
319 assert( self );
320
321 if( self->filename )
322 {
323 free( self->filename );
324 }
325
326
327 for( i=0; i<vector_size( self->glyphs ); ++i)
328 {
329 glyph = *(texture_glyph_t **) vector_get( self->glyphs, i );
330 texture_glyph_delete( glyph);
331 }
332
333 vector_delete( self->glyphs );
334 free( self );
335 }
336
337
338 // ----------------------------------------------- texture_font_load_glyphs ---
339 size_t
340 texture_font_load_glyphs( texture_font_t * self,
341 const wchar_t * charcodes )
342 {
343 size_t i, x, y, width, height, depth, w, h;
344 FT_Library library;
345 FT_Error error;
346 FT_Face face;
347 FT_Glyph ft_glyph;
348 FT_GlyphSlot slot;
349 FT_Bitmap ft_bitmap;
350
351 FT_UInt glyph_index;
352 texture_glyph_t *glyph;
353 ivec4 region;
354 size_t missed = 0;
355
356 assert( self );
357 assert( charcodes );
358
359
360 width = self->atlas->width;
361 height = self->atlas->height;
362 depth = self->atlas->depth;
363
364 if( !texture_font_load_face( &library, self->filename, self->size, &face ) )
365 {
366 return wcslen(charcodes);
367 }
368
369 /* Load each glyph */
370 for( i=0; i<wcslen(charcodes); ++i )
371 {
372 FT_Int32 flags = 0;
373 int ft_bitmap_width = 0;
374 int ft_bitmap_rows = 0;
375 int ft_bitmap_pitch = 0;
376 int ft_glyph_top = 0;
377 int ft_glyph_left = 0;
378 glyph_index = FT_Get_Char_Index( face, charcodes[i] );
379 // WARNING: We use texture-atlas depth to guess if user wants
380 // LCD subpixel rendering
381
382 if( self->outline_type > 0 )
383 {
384 flags |= FT_LOAD_NO_BITMAP;
385 }
386 else
387 {
388 flags |= FT_LOAD_RENDER;
389 }
390
391 if( !self->hinting )
392 {
393 flags |= FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT;
394 }
395 else
396 {
397 flags |= FT_LOAD_FORCE_AUTOHINT;
398 }
399
400
401 if( depth == 3 )
402 {
403 FT_Library_SetLcdFilter( library, FT_LCD_FILTER_LIGHT );
404 flags |= FT_LOAD_TARGET_LCD;
405 if( self->filtering )
406 {
407 FT_Library_SetLcdFilterWeights( library, self->lcd_weights );
408 }
409 }
410 error = FT_Load_Glyph( face, glyph_index, flags );
411 if( error )
412 {
413 fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n",
414 __LINE__, FT_Errors[error].code, FT_Errors[error].message );
415 FT_Done_Face( face );
416 FT_Done_FreeType( library );
417 return wcslen(charcodes)-i;
418 }
419
420
421 if( self->outline_type == 0 )
422 {
423 slot = face->glyph;
424 ft_bitmap = slot->bitmap;
425 ft_bitmap_width = slot->bitmap.width;
426 ft_bitmap_rows = slot->bitmap.rows;
427 ft_bitmap_pitch = slot->bitmap.pitch;
428 ft_glyph_top = slot->bitmap_top;
429 ft_glyph_left = slot->bitmap_left;
430 }
431 else
432 {
433 FT_Stroker stroker;
434 FT_BitmapGlyph ft_bitmap_glyph;
435 error = FT_Stroker_New( library, &stroker );
436 if( error )
437 {
438 fprintf(stderr, "FT_Error (0x%02x) : %s\n",
439 FT_Errors[error].code, FT_Errors[error].message);
440 FT_Done_Face( face );
441 FT_Stroker_Done( stroker );
442 FT_Done_FreeType( library );
443 return 0;
444 }
445 FT_Stroker_Set( stroker,
446 (int)(self->outline_thickness *64),
447 FT_STROKER_LINECAP_ROUND,
448 FT_STROKER_LINEJOIN_ROUND,
449 0);
450 error = FT_Get_Glyph( face->glyph, &ft_glyph);
451 if( error )
452 {
453 fprintf(stderr, "FT_Error (0x%02x) : %s\n",
454 FT_Errors[error].code, FT_Errors[error].message);
455 FT_Done_Face( face );
456 FT_Stroker_Done( stroker );
457 FT_Done_FreeType( library );
458 return 0;
459 }
460
461 if( self->outline_type == 1 )
462 {
463 error = FT_Glyph_Stroke( &ft_glyph, stroker, 1 );
464 }
465 else if ( self->outline_type == 2 )
466 {
467 error = FT_Glyph_StrokeBorder( &ft_glyph, stroker, 0, 1 );
468 }
469 else if ( self->outline_type == 3 )
470 {
471 error = FT_Glyph_StrokeBorder( &ft_glyph, stroker, 1, 1 );
472 }
473 if( error )
474 {
475 fprintf(stderr, "FT_Error (0x%02x) : %s\n",
476 FT_Errors[error].code, FT_Errors[error].message);
477 FT_Done_Face( face );
478 FT_Stroker_Done( stroker );
479 FT_Done_FreeType( library );
480 return 0;
481 }
482
483 if( depth == 1)
484 {
485 error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1);
486 if( error )
487 {
488 fprintf(stderr, "FT_Error (0x%02x) : %s\n",
489 FT_Errors[error].code, FT_Errors[error].message);
490 FT_Done_Face( face );
491 FT_Stroker_Done( stroker );
492 FT_Done_FreeType( library );
493 return 0;
494 }
495 }
496 else
497 {
498 error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_LCD, 0, 1);
499 if( error )
500 {
501 fprintf(stderr, "FT_Error (0x%02x) : %s\n",
502 FT_Errors[error].code, FT_Errors[error].message);
503 FT_Done_Face( face );
504 FT_Stroker_Done( stroker );
505 FT_Done_FreeType( library );
506 return 0;
507 }
508 }
509 ft_bitmap_glyph = (FT_BitmapGlyph) ft_glyph;
510 ft_bitmap = ft_bitmap_glyph->bitmap;
511 ft_bitmap_width = ft_bitmap.width;
512 ft_bitmap_rows = ft_bitmap.rows;
513 ft_bitmap_pitch = ft_bitmap.pitch;
514 ft_glyph_top = ft_bitmap_glyph->top;
515 ft_glyph_left = ft_bitmap_glyph->left;
516 FT_Stroker_Done(stroker);
517 }
518
519
520 // We want each glyph to be separated by at least one black pixel
521 // (for example for shader used in demo-subpixel.c)
522 w = ft_bitmap_width/depth + 1;
523 h = ft_bitmap_rows + 1;
524 region = texture_atlas_get_region( self->atlas, w, h );
525 if ( region.x < 0 )
526 {
527 missed++;
528 fprintf( stderr, "Texture atlas is full (line %d)\n", __LINE__ );
529 continue;
530 }
531 w = w - 1;
532 h = h - 1;
533 x = region.x;
534 y = region.y;
535 texture_atlas_set_region( self->atlas, x, y, w, h,
536 ft_bitmap.buffer, ft_bitmap.pitch );
537
538 glyph = texture_glyph_new( );
539 glyph->charcode = charcodes[i];
540 glyph->width = w;
541 glyph->height = h;
542 glyph->outline_type = self->outline_type;
543 glyph->outline_thickness = self->outline_thickness;
544 glyph->offset_x = ft_glyph_left;
545 glyph->offset_y = ft_glyph_top;
546 glyph->s0 = x/(float)width;
547 glyph->t0 = y/(float)height;
548 glyph->s1 = (x + glyph->width)/(float)width;
549 glyph->t1 = (y + glyph->height)/(float)height;
550
551 // Discard hinting to get advance
552 FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER | FT_LOAD_NO_HINTING);
553 slot = face->glyph;
554 glyph->advance_x = slot->advance.x/64.0;
555 glyph->advance_y = slot->advance.y/64.0;
556
557 vector_push_back( self->glyphs, &glyph );
558
559 if( self->outline_type > 0 )
560 {
561 FT_Done_Glyph( ft_glyph );
562 }
563 }
564 FT_Done_Face( face );
565 FT_Done_FreeType( library );
566 texture_atlas_upload( self->atlas );
567 texture_font_generate_kerning( self );
568 return missed;
569 }
570
571
572 // ------------------------------------------------- texture_font_get_glyph ---
573 texture_glyph_t *
574 texture_font_get_glyph( texture_font_t * self,
575 wchar_t charcode )
576 {
577 size_t i;
578 wchar_t buffer[2] = {0,0};
579 texture_glyph_t *glyph;
580
581 assert( self );
582
583 assert( self );
584 assert( self->filename );
585 assert( self->atlas );
586
587 /* Check if charcode has been already loaded */
588 for( i=0; i<self->glyphs->size; ++i )
589 {
590 glyph = *(texture_glyph_t **) vector_get( self->glyphs, i );
591 // If charcode is -1, we don't care about outline type or thickness
592 if( (glyph->charcode == charcode) &&
593 ((charcode == (wchar_t)(-1) ) ||
594 ((glyph->outline_type == self->outline_type) &&
595 (glyph->outline_thickness == self->outline_thickness)) ))
596 {
597 return glyph;
598 }
599 }
600
601 /* charcode -1 is special : it is used for line drawing (overline,
602 * underline, strikethrough) and background.
603 */
604 if( charcode == (wchar_t)(-1) )
605 {
606 size_t width = self->atlas->width;
607 size_t height = self->atlas->height;
608 ivec4 region = texture_atlas_get_region( self->atlas, 5, 5 );
609 texture_glyph_t * glyph = texture_glyph_new( );
610 static unsigned char data[4*4*3] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
611 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
612 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
613 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
614 if ( region.x < 0 )
615 {
616 fprintf( stderr, "Texture atlas is full (line %d)\n", __LINE__ );
617 return NULL;
618 }
619 texture_atlas_set_region( self->atlas, region.x, region.y, 4, 4, data, 0 );
620 glyph->charcode = (wchar_t)(-1);
621 glyph->s0 = (region.x+2)/(float)width;
622 glyph->t0 = (region.y+2)/(float)height;
623 glyph->s1 = (region.x+3)/(float)width;
624 glyph->t1 = (region.y+3)/(float)height;
625 vector_push_back( self->glyphs, &glyph );
626 return glyph; //*(texture_glyph_t **) vector_back( self->glyphs );
627 }
628
629 /* Glyph has not been already loaded */
630 buffer[0] = charcode;
631 if( texture_font_load_glyphs( self, buffer ) == 0 )
632 {
633 return *(texture_glyph_t **) vector_back( self->glyphs );
634 }
635 return NULL;
636 }
637
638