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