source: project/gl-font/gl_font.c @ 3733

Last change on this file since 3733 was 3733, checked in by thu, 14 years ago


File size: 13.5 KB
Line 
1/* was example1.c from the freetype distribution with "inspiration" from fltk */
2
3/* added by me (mt) :
4 * TODO try with XftFont ?
5 * TODO WIDTH and HEIGHT don't have to be fixed but computed from face size.
6 *
7 * to compile: cc font2ppm.c -I/usr/include/freetype2 -lfreetype -lm
8 * usage example: ./a.out /usr/share/fonts/truetype/freefont/FreeMono.ttf hello > image.ppm
9 */
10
11#include <stdio.h>
12#include <string.h>
13#include <math.h>
14#include <wchar.h>
15#include <locale.h>
16
17
18#include "gl_font.h"
19
20#define WIDTH   512
21#define HEIGHT  512
22
23static FT_Library ft_library;
24static int ft_library_initialized_flag = 0;
25
26/* The image is stored upside-down: the first byte is the
27 * bottom-left corner : this is how OpenGL stores textures.
28 */
29
30void
31alloc_image( unsigned char ** p, int width, int height )
32{
33  *p = malloc( width * height );
34  memset( *p, 0, width * height );
35}
36
37void
38free_image( unsigned char ** p )
39{
40  free( *p );
41  *p = NULL;
42}
43
44
45void
46alloc_glyphs_info( glyph_info_t ** p, int number )
47{
48  *p = malloc( sizeof(glyph_info_t) * number );
49  memset( *p, 0, sizeof(glyph_info_t) * number );
50}
51
52void
53free_glyphs_info( glyph_info_t ** p )
54{
55  free( *p );
56  *p = NULL;
57}
58
59void
60print_image_as_ppm( unsigned char * image, int width, int height )
61{
62  int i;
63 
64  printf("P3\n%d %d\n255\n", width, height);
65 
66  for ( i = 0 ; i < width * height ; i++ )
67  {
68    printf( "%d %d %d\n", image[i], image[i], image[i] );
69    putchar( '\n' );
70  }
71}
72
73
74void
75render_face_to_image( FT_Face face, unsigned char * image, glyph_info_t * glyphs_info, int width, int height )
76{
77  // lower-left corner of next bitmap:
78  int curx = 1;
79  int cury = 1;
80  // when a new row is started it goes here:
81  int maxy = 1;
82
83  int char_code;
84  for (char_code = 0; char_code < 256; char_code++)
85  {
86    FT_Bitmap * bitmap;
87    int w;
88    int h;
89    int i, j, x, y;
90    glyph_info_t * glyph_info = &glyphs_info[char_code];
91   
92    FT_Load_Char( face, char_code, FT_LOAD_RENDER );
93
94    glyph_info->advance = face->glyph->advance.x >> 6; 
95
96    if ( face->glyph->format != FT_GLYPH_FORMAT_BITMAP )
97    {
98      glyph_info->w = 0;
99      glyph_info->h = 0;
100      continue;
101    }
102   
103    bitmap = &face->glyph->bitmap;
104
105    w = glyph_info->w = bitmap->width;
106    h = glyph_info->h = bitmap->rows;
107
108    // Skip blank bitmaps
109    if (w < 1 || h < 1)
110    {
111      continue;
112    }
113   
114    // figure out where we will put the bitmap
115    if ( curx + w + 1 > WIDTH ) // start a new row
116    {
117      curx = 1;
118      cury = maxy+1;
119    }
120     
121    // figure out where next row must start
122    if ( cury + h > maxy )
123    {
124      maxy = cury+h;
125    }
126     
127    x = glyph_info->x = curx;
128    y = glyph_info->y = cury;
129    curx += w + 1;
130
131    /* copy the pixmap */
132    for (j = 0; j < h; j++)
133     for (i = 0; i < w; i++)
134       image[(y+j) * WIDTH + x + i] = bitmap->buffer[(h-j-1) * bitmap->pitch + i];
135//       image[(y+j) * WIDTH + x + i] = bitmap->buffer[(j) * bitmap->pitch + i];
136
137    glyph_info->left   = face->glyph->bitmap_left;
138    glyph_info->bottom = face->glyph->bitmap_top - h;
139  }
140}
141
142void
143render_glyph_to_image( FT_Face face, unsigned char * image, glyph_info_t * glyph_info, wchar_t code )
144{
145  // lower-left corner of next bitmap:
146  int curx = 1;
147  int cury = 1;
148  // when a new row is started it goes here:
149  int maxy = 1;
150
151  int char_code = code; /* TODO use directly code */
152  {
153    FT_Bitmap * bitmap;
154    int w;
155    int h;
156    int i, j, x, y;
157   
158    FT_Load_Char( face, code, FT_LOAD_RENDER );
159
160    glyph_info->advance = face->glyph->advance.x >> 6; 
161
162    if ( face->glyph->format != FT_GLYPH_FORMAT_BITMAP ) /* TODO draw something else */
163    {
164      glyph_info->w = 0;
165      glyph_info->h = 0;
166    }
167   
168    bitmap = &face->glyph->bitmap;
169
170    w = glyph_info->w = bitmap->width;
171    h = glyph_info->h = bitmap->rows;
172
173    // Skip blank bitmaps
174    if (w < 1 || h < 1)
175    {
176    }
177   
178    // figure out where we will put the bitmap
179    if ( curx + w + 1 > WIDTH ) // start a new row
180    {
181      curx = 1;
182      cury = maxy+1;
183    }
184     
185    // figure out where next row must start
186    if ( cury + h > maxy )
187    {
188      maxy = cury+h;
189    }
190     
191    x = glyph_info->x = curx;
192    y = glyph_info->y = cury;
193
194    /* copy the pixmap */
195    for (j = 0; j < h; j++)
196     for (i = 0; i < w; i++)
197       image[(y+j) * WIDTH + x + i] = bitmap->buffer[(h-j-1) * bitmap->pitch + i];
198//       image[(y+j) * WIDTH + x + i] = bitmap->buffer[(j) * bitmap->pitch + i];
199
200    glyph_info->left   = face->glyph->bitmap_left;
201    glyph_info->bottom = face->glyph->bitmap_top - h;
202  }
203}
204
205int
206next_pow2( int a )
207{
208  int n;
209  for (n=1 ; n<a ; n<<=1);
210  return n;
211}
212
213static int listbase;
214static GLuint texture;
215
216void
217create_display_lists( unsigned char * image, glyph_info_t * glyphs_info, int maxy )
218{
219  int width;
220  int height;
221  int char_code;
222  float umul;
223  float vmul;
224  glPushAttrib(GL_TEXTURE_BIT);
225  glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
226
227  listbase = glGenLists(256);
228 
229  // generate the OpenGL texture map we will use:
230
231  height = next_pow2( maxy );
232  width = WIDTH;
233
234  glGenTextures( 1, &texture );
235
236  glBindTexture( GL_TEXTURE_2D, texture );
237  glPixelStorei( GL_UNPACK_ALIGNMENT,   1 );
238  glPixelStorei( GL_UNPACK_ROW_LENGTH,  0 );
239  glPixelStorei( GL_UNPACK_SKIP_PIXELS, 0 );
240  glPixelStorei( GL_UNPACK_SKIP_ROWS,   0 );
241  glTexImage2D( GL_TEXTURE_2D, 0, GL_INTENSITY8, width, height, 0,
242                GL_LUMINANCE, GL_UNSIGNED_BYTE, image );
243  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
244  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
245  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,     GL_CLAMP );
246  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,     GL_CLAMP );
247
248  // Now build the display lists
249  umul = 1.0f/width;
250  vmul = 1.0f/height;
251  for (char_code = 0; char_code < 256; char_code++)
252  {
253    glyph_info_t * t = &glyphs_info[char_code];
254
255    // Create the display list for this character:
256    glNewList(listbase + char_code, GL_COMPILE);
257
258    // Draw a texturemapped rectangle:
259    if (t->w > 0 && t->h > 0)
260    {
261      glBegin( GL_QUADS );
262      glTexCoord2f( t->x * umul, t->y * vmul);
263      glVertex2i(   t->left, t->bottom);
264      glTexCoord2f((t->x + t->w) * umul, t->y * vmul);
265      glVertex2i(   t->left + t->w, t->bottom);
266      glTexCoord2f((t->x + t->w) * umul, (t->y + t->h) * vmul);
267      glVertex2i(   t->left +t->w,t->bottom + t->h);
268      glTexCoord2f( t->x * umul, (t->y + t->h) * vmul);
269      glVertex2i(   t->left, t->bottom + t->h);
270      glEnd();
271    }
272    // move the origin:
273    glTranslatef(t->advance, 0, 0);
274
275    glEndList();
276  }
277
278  glPopClientAttrib();
279  glPopAttrib();
280}
281
282void
283draw_glyph( unsigned char * image, font_unicode_t * font, glyph_info_t * glyph_info, int maxy, wchar_t char_code )
284{
285  int width;
286  int height;
287  float umul;
288  float vmul;
289  glPushAttrib(GL_TEXTURE_BIT);
290  glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
291  glyph_info_t * t = glyph_info;
292
293  // generate the OpenGL texture map we will use:
294
295  height = next_pow2( maxy );
296  width = WIDTH;
297
298  glBindTexture( GL_TEXTURE_2D, font->texture_id );
299  glPixelStorei( GL_UNPACK_ALIGNMENT,   1 );
300  glPixelStorei( GL_UNPACK_ROW_LENGTH,  0 );
301  glPixelStorei( GL_UNPACK_SKIP_PIXELS, 0 );
302  glPixelStorei( GL_UNPACK_SKIP_ROWS,   0 );
303  glTexImage2D( GL_TEXTURE_2D, 0, GL_INTENSITY8, width, height, 0,
304                GL_LUMINANCE, GL_UNSIGNED_BYTE, image );
305  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
306  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
307  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,     GL_CLAMP );
308  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,     GL_CLAMP );
309
310  umul = 1.0f/width;
311  vmul = 1.0f/height;
312 
313  // Draw a texturemapped rectangle:
314  if (t->w > 0 && t->h > 0)
315  {
316    glBegin( GL_QUADS );
317    glTexCoord2f( t->x * umul, t->y * vmul);
318    glVertex2i(   t->left, t->bottom);
319    glTexCoord2f((t->x + t->w) * umul, t->y * vmul);
320    glVertex2i(   t->left + t->w, t->bottom);
321    glTexCoord2f((t->x + t->w) * umul, (t->y + t->h) * vmul);
322    glVertex2i(   t->left +t->w,t->bottom + t->h);
323    glTexCoord2f( t->x * umul, (t->y + t->h) * vmul);
324    glVertex2i(   t->left, t->bottom + t->h);
325    glEnd();
326  }
327  // move the origin:
328  glTranslatef(t->advance, 0, 0);
329
330  glPopClientAttrib();
331  glPopAttrib();
332}
333
334/* version of gl_print that handles ANSI escape sequences */
335void
336gl_print_ansi( char * text )
337{
338  char * c;
339 
340  glPushMatrix();
341  glEnable( GL_TEXTURE_2D );
342  glBindTexture( GL_TEXTURE_2D, texture );
343  glListBase(listbase);
344  for ( c = text ; *c ; c++ )
345  {
346    switch ( *c )
347    {
348      case 0x1b :
349              ++c;
350              if ( 0 == strncmp( c, "[1;33m", 6 ) ) /* yellow */
351              {
352                glColor3f( 1.0f, 1.0f, 0.0f );
353                c += 5;
354                break;
355              }
356              if ( 0 == strncmp( c, "[0m", 3 ) )    /* nothing */
357              {
358                glColor3f( 1.0f, 1.0f, 1.0f ); /* TODO default */
359                c += 2;
360                break;
361              }
362      case '\n' :
363              glPopMatrix();
364              glTranslatef(0, -18, 0); /* TODO 18 depends on the font... */
365              glPushMatrix();
366              break;
367      default:
368              /* glCallList( *c ); FIXME why doesn't it work ? */ 
369              glCallLists( 1, GL_UNSIGNED_BYTE, c );
370    }
371  }
372  glDisable( GL_TEXTURE_2D );
373  glPopMatrix();
374   
375}
376
377void
378gl_print_ascii( char * text )
379{
380  int len; /* length of the current line ('till the next \n). */
381  int rem; /* length of the not yet printed text. */
382  char * current;
383  char * next;
384
385  current = text;
386  rem = strlen( text );
387  do 
388  {
389    next = strchr( current, '\n' );
390    if ( next ) {
391      len  = next - current;
392
393      glPushMatrix();
394      glEnable( GL_TEXTURE_2D );
395      glBindTexture( GL_TEXTURE_2D, texture );
396      glListBase(listbase);
397      glCallLists( len, GL_UNSIGNED_BYTE, current );
398      glDisable( GL_TEXTURE_2D );
399      glPopMatrix();
400   
401      glTranslatef(0, -18, 0); /* TODO 18 depends on the font... */
402
403      current = next + 1;
404      rem -= len + 1;
405    }
406    else
407    {
408      len = strlen( current );
409     
410      glEnable( GL_TEXTURE_2D );
411      glBindTexture( GL_TEXTURE_2D, texture );
412      glListBase(listbase);
413      glCallLists( len, GL_UNSIGNED_BYTE, current );
414      glDisable( GL_TEXTURE_2D );
415
416      rem = 0;
417    }
418  } while ( rem );
419}
420
421void
422gl_print( font_unicode_t * font, char * text )
423{
424  static unsigned char image[WIDTH * HEIGHT];
425  wchar_t wtext[512]; /* TODO make it depends on text length */
426  int i;
427  wchar_t * c;
428  if (!setlocale(LC_CTYPE, ""))
429  {
430    fprintf(stderr, "Can't set the specified locale! "
431        "Check LANG, LC_CTYPE, LC_ALL.\n");
432    return;
433  }
434  mbstowcs( wtext, text, 512 );
435  glPushMatrix();
436  glEnable( GL_TEXTURE_2D );
437  glBindTexture( GL_TEXTURE_2D, texture );
438  for ( c = wtext ; *c ; c++ )
439  {
440    switch ( *c )
441    {
442      case 0x1b :
443              ++c;
444              if ( 0 == wcsncmp( c, L"[1;33m", 6 ) ) /* yellow */
445              {
446                glColor3f( 1.0f, 1.0f, 0.0f );
447                c += 5;
448                break;
449              }
450              if ( 0 == wcsncmp( c, L"[0m", 3 ) )    /* nothing */
451              {
452                glColor3f( 1.0f, 1.0f, 1.0f ); /* TODO default */
453                c += 2;
454                break;
455              }
456      case '\n' :
457              glPopMatrix();
458              glTranslatef(0, -18, 0); /* TODO 18 depends on the font... */
459              glPushMatrix();
460              break;
461      default:
462              render_glyph_to_image( font->face, image, &font->glyph_info, *c ); /* remove glyph_info from the struct */
463              draw_glyph( image, font, &font->glyph_info, 64, 0 ); /* TODO where does 64 come from ?, remove the 0 */
464    }
465  }
466  glDisable( GL_TEXTURE_2D );
467  glPopMatrix();
468}
469
470#define FONT_PATH "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf"
471
472void
473create_font_from_path_ascii( char * font_path )
474{
475  FT_Library library;
476  FT_Face    face;
477  unsigned char * image;
478  glyph_info_t *  glyphs_info;
479
480  if ( FT_Init_FreeType( &library ) )
481  {
482    fprintf( stderr, "error when setting char size\n" );
483    exit( 1 );
484  }
485
486  if ( FT_New_Face( library, font_path, 0, &face ) )
487  {
488    fprintf( stderr, "error when reading new face\n" );
489    exit( 1 );
490  }
491
492  /* use 16pt at 72dpi */
493  if ( FT_Set_Char_Size( face, 16 * 64, 0, 72, 72 ) )
494  {
495    fprintf( stderr, "error when setting char size\n" );
496    exit( 1 );
497  }
498 
499  alloc_image( &image, WIDTH, HEIGHT );
500  alloc_glyphs_info( &glyphs_info, 256 );
501 
502  render_face_to_image( face, image, glyphs_info, WIDTH, HEIGHT );
503  //print_image_as_ppm( image, WIDTH, HEIGHT );
504  create_display_lists( image, glyphs_info, 64 );
505
506  free_image( &image );
507  free_glyphs_info( &glyphs_info );
508
509  FT_Done_Face( face );
510  FT_Done_FreeType( library );
511}
512
513font_unicode_t *
514create_font_from_path( char * font_path )
515{
516  font_unicode_t * font = (font_unicode_t *)malloc( sizeof(font_unicode_t) );
517
518  if ( !ft_library_initialized_flag )
519  {
520    if ( FT_Init_FreeType( &ft_library ) )
521    {
522      fprintf( stderr, "error when setting char size\n" );
523      exit( 1 );
524    }
525    ft_library_initialized_flag = 1;
526  }
527
528  if ( FT_New_Face( ft_library, font_path, 0, &font->face ) )
529  {
530    fprintf( stderr, "error when reading new face\n" );
531    exit( 1 );
532  }
533
534  /* use 16pt at 72dpi */
535  if ( FT_Set_Char_Size( font->face, 16 * 64, 0, 72, 72 ) )
536  {
537    fprintf( stderr, "error when setting char size\n" );
538    exit( 1 );
539  }
540 
541  alloc_image( &font->image, WIDTH, HEIGHT ); /* TODO no need for that much here. */
542 
543  /* free_image( &font->image ); TODO when writing code for freeing ressource
544
545  FT_Done_Face( font->face );
546  FT_Done_FreeType( ft_library );
547  ft_library_initialized_flag = 0;
548  */
549
550  glGenTextures( 1, &font->texture_id ); /* TODO release it */
551  return font;
552}
553
554/* TODO Use Xft here */
555void
556create_font( void )
557{
558  create_font_from_path_ascii( FONT_PATH );
559}
560
Note: See TracBrowser for help on using the repository browser.