1 /*
2     glfont:  An example of using the SDL_ttf library with OpenGL.
3     Copyright (C) 1997-2004 Sam Lantinga
4 
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Library General Public
7     License as published by the Free Software Foundation; either
8     version 2 of the License, or (at your option) any later version.
9 
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Library General Public License for more details.
14 
15     You should have received a copy of the GNU Library General Public
16     License along with this library; if not, write to the Free
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 
19     The SDL_GL_* functions in this file are available in the public domain.
20 
21     Sam Lantinga
22     slouken@libsdl.org
23 */
24 
25 /* $Id: glfont.c 2429 2006-05-14 21:03:44Z slouken $ */
26 
27 /* A simple program to test the text rendering feature of the TTF library */
28 
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 
33 #include "SDL.h"
34 #include "SDL_ttf.h"
35 
36 #ifdef HAVE_OPENGL
37 
38 #include "SDL_opengl.h"
39 
40 #define DEFAULT_PTSIZE 18
41 #define DEFAULT_TEXT "The quick brown fox jumped over the lazy dog"
42 #define NUM_COLORS      256
43 
44 static char *Usage =
45 "Usage: %s [-utf8|-unicode] [-b] [-i] [-u] [-fgcol r,g,b] [-bgcol r,g,b] \
46 <font>.ttf [ptsize] [text]\n";
47 
SDL_GL_Enter2DMode()48 void SDL_GL_Enter2DMode()
49 {
50  SDL_Surface *screen = SDL_GetVideoSurface();
51 
52  /* Note, there may be other things you need to change,
53     depending on how you have your OpenGL state set up.
54  */
55  glPushAttrib(GL_ENABLE_BIT);
56  glDisable(GL_DEPTH_TEST);
57  glDisable(GL_CULL_FACE);
58  glEnable(GL_TEXTURE_2D);
59 
60  /* This allows alpha blending of 2D textures with the scene */
61  glEnable(GL_BLEND);
62  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
63 
64  glViewport(0, 0, screen->w, screen->h);
65 
66  glMatrixMode(GL_PROJECTION);
67  glPushMatrix();
68  glLoadIdentity();
69 
70  glOrtho(0.0, (GLdouble)screen->w, (GLdouble)screen->h, 0.0, 0.0, 1.0);
71 
72  glMatrixMode(GL_MODELVIEW);
73  glPushMatrix();
74  glLoadIdentity();
75 
76  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
77 }
78 
SDL_GL_Leave2DMode()79 void SDL_GL_Leave2DMode()
80 {
81  glMatrixMode(GL_MODELVIEW);
82  glPopMatrix();
83 
84  glMatrixMode(GL_PROJECTION);
85  glPopMatrix();
86 
87  glPopAttrib();
88 }
89 
90 /* Quick utility function for texture creation */
power_of_two(int input)91 static int power_of_two(int input)
92 {
93  int value = 1;
94 
95  while ( value < input ) {
96    value <<= 1;
97  }
98  return value;
99 }
100 
SDL_GL_LoadTexture(SDL_Surface * surface,GLfloat * texcoord)101 GLuint SDL_GL_LoadTexture(SDL_Surface *surface, GLfloat *texcoord)
102 {
103  GLuint texture;
104  int w, h;
105  SDL_Surface *image;
106  SDL_Rect area;
107  Uint32 saved_flags;
108  Uint8  saved_alpha;
109 
110  /* Use the surface width and height expanded to powers of 2 */
111  w = power_of_two(surface->w);
112  h = power_of_two(surface->h);
113  texcoord[0] = 0.0f;     /* Min X */
114  texcoord[1] = 0.0f;     /* Min Y */
115  texcoord[2] = (GLfloat)surface->w / w;  /* Max X */
116  texcoord[3] = (GLfloat)surface->h / h;  /* Max Y */
117 
118  image = SDL_CreateRGBSurface(
119      SDL_SWSURFACE,
120      w, h,
121      32,
122 #if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */
123      0x000000FF,
124      0x0000FF00,
125      0x00FF0000,
126      0xFF000000
127 #else
128      0xFF000000,
129      0x00FF0000,
130      0x0000FF00,
131      0x000000FF
132 #endif
133           );
134  if ( image == NULL ) {
135    return 0;
136  }
137 
138  /* Save the alpha blending attributes */
139  saved_flags = surface->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
140  saved_alpha = surface->format->alpha;
141  if ( (saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) {
142    SDL_SetAlpha(surface, 0, 0);
143  }
144 
145  /* Copy the surface into the GL texture image */
146  area.x = 0;
147  area.y = 0;
148  area.w = surface->w;
149  area.h = surface->h;
150  SDL_BlitSurface(surface, &area, image, &area);
151 
152  /* Restore the alpha blending attributes */
153  if ( (saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) {
154    SDL_SetAlpha(surface, saved_flags, saved_alpha);
155  }
156 
157  /* Create an OpenGL texture for the image */
158  glGenTextures(1, &texture);
159  glBindTexture(GL_TEXTURE_2D, texture);
160  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
161  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
162  glTexImage2D(GL_TEXTURE_2D,
163         0,
164         GL_RGBA,
165         w, h,
166         0,
167         GL_RGBA,
168         GL_UNSIGNED_BYTE,
169         image->pixels);
170  SDL_FreeSurface(image); /* No longer needed */
171 
172  return texture;
173 }
174 
cleanup(int exitcode)175 static void cleanup(int exitcode)
176 {
177  TTF_Quit();
178  SDL_Quit();
179  exit(exitcode);
180 }
181 
main(int argc,char * argv[])182 int main(int argc, char *argv[])
183 {
184  char *argv0 = argv[0];
185  SDL_Surface *screen;
186  TTF_Font *font;
187  SDL_Surface *text;
188  int ptsize;
189  int i, done;
190  SDL_Color white = { 0xFF, 0xFF, 0xFF, 0 };
191  SDL_Color black = { 0x00, 0x00, 0x00, 0 };
192  SDL_Color *forecol;
193  SDL_Color *backcol;
194  GLenum gl_error;
195  GLuint texture;
196  int x, y, w, h;
197  GLfloat texcoord[4];
198  GLfloat texMinX, texMinY;
199  GLfloat texMaxX, texMaxY;
200         float color[8][3]= {{ 1.0,  1.0,  0.0},
201          { 1.0,  0.0,  0.0},
202          { 0.0,  0.0,  0.0},
203          { 0.0,  1.0,  0.0},
204          { 0.0,  1.0,  1.0},
205          { 1.0,  1.0,  1.0},
206          { 1.0,  0.0,  1.0},
207          { 0.0,  0.0,  1.0}};
208  float cube[8][3]= {{ 0.5,  0.5, -0.5},
209         { 0.5, -0.5, -0.5},
210         {-0.5, -0.5, -0.5},
211         {-0.5,  0.5, -0.5},
212         {-0.5,  0.5,  0.5},
213         { 0.5,  0.5,  0.5},
214         { 0.5, -0.5,  0.5},
215         {-0.5, -0.5,  0.5}};
216  SDL_Event event;
217  int renderstyle;
218  int dump;
219  enum {
220    RENDER_LATIN1,
221    RENDER_UTF8,
222    RENDER_UNICODE
223  } rendertype;
224  char *message;
225 
226  /* Look for special execution mode */
227  dump = 0;
228  /* Look for special rendering types */
229  renderstyle = TTF_STYLE_NORMAL;
230  rendertype = RENDER_LATIN1;
231  /* Default is black and white */
232  forecol = &black;
233  backcol = &white;
234  for ( i=1; argv[i] && argv[i][0] == '-'; ++i ) {
235    if ( strcmp(argv[i], "-utf8") == 0 ) {
236      rendertype = RENDER_UTF8;
237    } else
238    if ( strcmp(argv[i], "-unicode") == 0 ) {
239      rendertype = RENDER_UNICODE;
240    } else
241    if ( strcmp(argv[i], "-b") == 0 ) {
242      renderstyle |= TTF_STYLE_BOLD;
243    } else
244    if ( strcmp(argv[i], "-i") == 0 ) {
245      renderstyle |= TTF_STYLE_ITALIC;
246    } else
247    if ( strcmp(argv[i], "-u") == 0 ) {
248      renderstyle |= TTF_STYLE_UNDERLINE;
249    } else
250    if ( strcmp(argv[i], "-dump") == 0 ) {
251      dump = 1;
252    } else
253    if ( strcmp(argv[i], "-fgcol") == 0 ) {
254      int r, g, b;
255      if ( sscanf (argv[++i], "%d,%d,%d", &r, &g, &b) != 3 ) {
256        fprintf(stderr, Usage, argv0);
257        return(1);
258      }
259      forecol->r = (Uint8)r;
260      forecol->g = (Uint8)g;
261      forecol->b = (Uint8)b;
262    } else
263    if ( strcmp(argv[i], "-bgcol") == 0 ) {
264      int r, g, b;
265      if ( sscanf (argv[++i], "%d,%d,%d", &r, &g, &b) != 3 ) {
266        fprintf(stderr, Usage, argv0);
267        return(1);
268      }
269      backcol->r = (Uint8)r;
270      backcol->g = (Uint8)g;
271      backcol->b = (Uint8)b;
272    } else {
273      fprintf(stderr, Usage, argv0);
274      return(1);
275    }
276  }
277  argv += i;
278  argc -= i;
279 
280  /* Check usage */
281  if ( ! argv[0] ) {
282    fprintf(stderr, Usage, argv0);
283    return(1);
284  }
285 
286  /* Initialize SDL */
287  if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
288    fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
289    return(2);
290  }
291 
292  /* Initialize the TTF library */
293  if ( TTF_Init() < 0 ) {
294    fprintf(stderr, "Couldn't initialize TTF: %s\n",SDL_GetError());
295    SDL_Quit();
296    return(2);
297  }
298 
299  /* Open the font file with the requested point size */
300  ptsize = 0;
301  if ( argc > 1 ) {
302    ptsize = atoi(argv[1]);
303  }
304  if ( ptsize == 0 ) {
305    i = 2;
306    ptsize = DEFAULT_PTSIZE;
307  } else {
308    i = 3;
309  }
310  font = TTF_OpenFont(argv[0], ptsize);
311  if ( font == NULL ) {
312    fprintf(stderr, "Couldn't load %d pt font from %s: %s\n",
313          ptsize, argv[0], SDL_GetError());
314    cleanup(2);
315  }
316  TTF_SetFontStyle(font, renderstyle);
317 
318  if( dump ) {
319    for( i = 48; i < 123; i++ ) {
320      SDL_Surface* glyph = NULL;
321 
322      glyph = TTF_RenderGlyph_Shaded( font, i, *forecol, *backcol );
323 
324      if( glyph ) {
325        char outname[64];
326        sprintf( outname, "glyph-%d.bmp", i );
327        SDL_SaveBMP( glyph, outname );
328      }
329 
330    }
331    cleanup(0);
332  }
333 
334  /* Set a 640x480 video mode */
335  screen = SDL_SetVideoMode(640, 480, 0, SDL_OPENGL);
336  if ( screen == NULL ) {
337    fprintf(stderr, "Couldn't set 640x480 OpenGL mode: %s\n",
338              SDL_GetError());
339    cleanup(2);
340  }
341 
342  /* Render and center the message */
343  if ( argc > 2 ) {
344    message = argv[2];
345  } else {
346    message = DEFAULT_TEXT;
347  }
348  switch (rendertype) {
349      case RENDER_LATIN1:
350    text = TTF_RenderText_Blended(font, message, *forecol);
351    break;
352 
353      case RENDER_UTF8:
354    text = TTF_RenderUTF8_Blended(font, message, *forecol);
355    break;
356 
357      case RENDER_UNICODE:
358    {
359      /* This doesn't actually work because you can't pass
360         UNICODE text in via command line, AFAIK, but...
361       */
362      Uint16 unicode_text[BUFSIZ];
363      int index;
364      for ( index = 0; (message[0] || message[1]); ++index ) {
365        unicode_text[index]  = ((Uint8 *)message)[0];
366        unicode_text[index] <<= 8;
367        unicode_text[index] |= ((Uint8 *)message)[1];
368        message += 2;
369      }
370      text = TTF_RenderUNICODE_Blended(font,
371          unicode_text, *forecol);
372    }
373    break;
374      default:
375    text = NULL; /* This shouldn't happen */
376    break;
377  }
378  if ( text == NULL ) {
379    fprintf(stderr, "Couldn't render text: %s\n", SDL_GetError());
380    TTF_CloseFont(font);
381    cleanup(2);
382  }
383  x = (screen->w - text->w)/2;
384  y = (screen->h - text->h)/2;
385  w = text->w;
386  h = text->h;
387  printf("Font is generally %d big, and string is %hd big\n",
388            TTF_FontHeight(font), text->h);
389 
390  /* Convert the text into an OpenGL texture */
391  glGetError();
392  texture = SDL_GL_LoadTexture(text, texcoord);
393  if ( (gl_error = glGetError()) != GL_NO_ERROR ) {
394    /* If this failed, the text may exceed texture size limits */
395    printf("Warning: Couldn't create texture: 0x%x\n", gl_error);
396  }
397 
398  /* Make texture coordinates easy to understand */
399  texMinX = texcoord[0];
400  texMinY = texcoord[1];
401  texMaxX = texcoord[2];
402  texMaxY = texcoord[3];
403 
404  /* We don't need the original text surface anymore */
405  SDL_FreeSurface(text);
406 
407  /* Initialize the GL state */
408  glViewport( 0, 0, screen->w, screen->h );
409  glMatrixMode( GL_PROJECTION );
410  glLoadIdentity( );
411 
412  glOrtho( -2.0, 2.0, -2.0, 2.0, -20.0, 20.0 );
413 
414  glMatrixMode( GL_MODELVIEW );
415  glLoadIdentity( );
416 
417  glEnable(GL_DEPTH_TEST);
418 
419  glDepthFunc(GL_LESS);
420 
421  glShadeModel(GL_SMOOTH);
422 
423  /* Wait for a keystroke, and blit text on mouse press */
424  done = 0;
425  while ( ! done ) {
426    while ( SDL_PollEvent(&event) ) {
427      switch (event.type) {
428          case SDL_MOUSEMOTION:
429        x = event.motion.x - w/2;
430        y = event.motion.y - h/2;
431        break;
432 
433          case SDL_KEYDOWN:
434          case SDL_QUIT:
435        done = 1;
436        break;
437          default:
438        break;
439      }
440    }
441 
442    /* Clear the screen */
443    glClearColor(1.0, 1.0, 1.0, 1.0);
444    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
445 
446    /* Draw the spinning cube */
447    glBegin( GL_QUADS );
448 
449      glColor3fv(color[0]);
450      glVertex3fv(cube[0]);
451      glColor3fv(color[1]);
452      glVertex3fv(cube[1]);
453      glColor3fv(color[2]);
454      glVertex3fv(cube[2]);
455      glColor3fv(color[3]);
456      glVertex3fv(cube[3]);
457 
458      glColor3fv(color[3]);
459      glVertex3fv(cube[3]);
460      glColor3fv(color[4]);
461      glVertex3fv(cube[4]);
462      glColor3fv(color[7]);
463      glVertex3fv(cube[7]);
464      glColor3fv(color[2]);
465      glVertex3fv(cube[2]);
466 
467      glColor3fv(color[0]);
468      glVertex3fv(cube[0]);
469      glColor3fv(color[5]);
470      glVertex3fv(cube[5]);
471      glColor3fv(color[6]);
472      glVertex3fv(cube[6]);
473      glColor3fv(color[1]);
474      glVertex3fv(cube[1]);
475 
476      glColor3fv(color[5]);
477      glVertex3fv(cube[5]);
478      glColor3fv(color[4]);
479      glVertex3fv(cube[4]);
480      glColor3fv(color[7]);
481      glVertex3fv(cube[7]);
482      glColor3fv(color[6]);
483      glVertex3fv(cube[6]);
484 
485      glColor3fv(color[5]);
486      glVertex3fv(cube[5]);
487      glColor3fv(color[0]);
488      glVertex3fv(cube[0]);
489      glColor3fv(color[3]);
490      glVertex3fv(cube[3]);
491      glColor3fv(color[4]);
492      glVertex3fv(cube[4]);
493 
494      glColor3fv(color[6]);
495      glVertex3fv(cube[6]);
496      glColor3fv(color[1]);
497      glVertex3fv(cube[1]);
498      glColor3fv(color[2]);
499      glVertex3fv(cube[2]);
500      glColor3fv(color[7]);
501      glVertex3fv(cube[7]);
502    glEnd( );
503 
504    /* Rotate the cube */
505    glMatrixMode(GL_MODELVIEW);
506    glRotatef(5.0, 1.0, 1.0, 1.0);
507 
508    /* Show the text on the screen */
509    SDL_GL_Enter2DMode();
510    glBindTexture(GL_TEXTURE_2D, texture);
511    glBegin(GL_TRIANGLE_STRIP);
512    glTexCoord2f(texMinX, texMinY); glVertex2i(x,   y  );
513    glTexCoord2f(texMaxX, texMinY); glVertex2i(x+w, y  );
514    glTexCoord2f(texMinX, texMaxY); glVertex2i(x,   y+h);
515    glTexCoord2f(texMaxX, texMaxY); glVertex2i(x+w, y+h);
516    glEnd();
517    SDL_GL_Leave2DMode();
518 
519    /* Swap the buffers so everything is visible */
520    SDL_GL_SwapBuffers( );
521  }
522  TTF_CloseFont(font);
523  cleanup(0);
524 
525  /* Not reached, but fixes compiler warnings */
526  return 0;
527 }
528 
529 #else /* HAVE_OPENGL */
530 
main(int argc,char * argv[])531 int main(int argc, char *argv[])
532 {
533  printf("No OpenGL support on this system\n");
534  return 1;
535 }
536 
537 #endif /* HAVE_OPENGL */
538