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