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