1 /*
2   glfont:  An example of using the SDL_ttf library with OpenGL.
3   Copyright (C) 2001-2019 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 #define TTF_GLFONT_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 static 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 static 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 static 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 = NULL;
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, TTF_GLFONT_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, TTF_GLFONT_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, TTF_GLFONT_USAGE, argv0);
270             return(1);
271         }
272     }
273     argv += i;
274     argc -= i;
275 
276     /* Check usage */
277     if (!argv[0]) {
278         fprintf(stderr, TTF_GLFONT_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     }
373     if (text == NULL) {
374         fprintf(stderr, "Couldn't render text: %s\n", SDL_GetError());
375         TTF_CloseFont(font);
376         cleanup(2);
377     }
378     x = (WIDTH - text->w)/2;
379     y = (HEIGHT - text->h)/2;
380     w = text->w;
381     h = text->h;
382     printf("Font is generally %d big, and string is %d big\n",
383                         TTF_FontHeight(font), text->h);
384 
385     /* Convert the text into an OpenGL texture */
386     glGetError();
387     texture = SDL_GL_LoadTexture(text, texcoord);
388     if ((gl_error = glGetError()) != GL_NO_ERROR) {
389         /* If this failed, the text may exceed texture size limits */
390         printf("Warning: Couldn't create texture: 0x%x\n", gl_error);
391     }
392 
393     /* Make texture coordinates easy to understand */
394     texMinX = texcoord[0];
395     texMinY = texcoord[1];
396     texMaxX = texcoord[2];
397     texMaxY = texcoord[3];
398 
399     /* We don't need the original text surface anymore */
400     SDL_FreeSurface(text);
401 
402     /* Initialize the GL state */
403     glViewport(0, 0, WIDTH, HEIGHT);
404     glMatrixMode(GL_PROJECTION);
405     glLoadIdentity();
406 
407     glOrtho(-2.0, 2.0, -2.0, 2.0, -20.0, 20.0);
408 
409     glMatrixMode(GL_MODELVIEW);
410     glLoadIdentity();
411 
412     glEnable(GL_DEPTH_TEST);
413 
414     glDepthFunc(GL_LESS);
415 
416     glShadeModel(GL_SMOOTH);
417 
418     /* Wait for a keystroke, and blit text on mouse press */
419     done = 0;
420     while (!done) {
421         while (SDL_PollEvent(&event)) {
422             switch (event.type) {
423                 case SDL_MOUSEMOTION:
424                 x = event.motion.x - w/2;
425                 y = event.motion.y - h/2;
426                 break;
427 
428                 case SDL_KEYDOWN:
429                 case SDL_QUIT:
430                 done = 1;
431                 break;
432                 default:
433                 break;
434             }
435         }
436 
437         /* Clear the screen */
438         glClearColor(1.0, 1.0, 1.0, 1.0);
439         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
440 
441         /* Draw the spinning cube */
442         glBegin(GL_QUADS);
443 
444             glColor3fv(color[0]);
445             glVertex3fv(cube[0]);
446             glColor3fv(color[1]);
447             glVertex3fv(cube[1]);
448             glColor3fv(color[2]);
449             glVertex3fv(cube[2]);
450             glColor3fv(color[3]);
451             glVertex3fv(cube[3]);
452 
453             glColor3fv(color[3]);
454             glVertex3fv(cube[3]);
455             glColor3fv(color[4]);
456             glVertex3fv(cube[4]);
457             glColor3fv(color[7]);
458             glVertex3fv(cube[7]);
459             glColor3fv(color[2]);
460             glVertex3fv(cube[2]);
461 
462             glColor3fv(color[0]);
463             glVertex3fv(cube[0]);
464             glColor3fv(color[5]);
465             glVertex3fv(cube[5]);
466             glColor3fv(color[6]);
467             glVertex3fv(cube[6]);
468             glColor3fv(color[1]);
469             glVertex3fv(cube[1]);
470 
471             glColor3fv(color[5]);
472             glVertex3fv(cube[5]);
473             glColor3fv(color[4]);
474             glVertex3fv(cube[4]);
475             glColor3fv(color[7]);
476             glVertex3fv(cube[7]);
477             glColor3fv(color[6]);
478             glVertex3fv(cube[6]);
479 
480             glColor3fv(color[5]);
481             glVertex3fv(cube[5]);
482             glColor3fv(color[0]);
483             glVertex3fv(cube[0]);
484             glColor3fv(color[3]);
485             glVertex3fv(cube[3]);
486             glColor3fv(color[4]);
487             glVertex3fv(cube[4]);
488 
489             glColor3fv(color[6]);
490             glVertex3fv(cube[6]);
491             glColor3fv(color[1]);
492             glVertex3fv(cube[1]);
493             glColor3fv(color[2]);
494             glVertex3fv(cube[2]);
495             glColor3fv(color[7]);
496             glVertex3fv(cube[7]);
497 
498         glEnd();
499 
500         /* Rotate the cube */
501         glMatrixMode(GL_MODELVIEW);
502         glRotatef(5.0, 1.0, 1.0, 1.0);
503 
504         /* Show the text on the screen */
505         SDL_GL_Enter2DMode(WIDTH, HEIGHT);
506         glBindTexture(GL_TEXTURE_2D, texture);
507         glBegin(GL_TRIANGLE_STRIP);
508         glTexCoord2f(texMinX, texMinY); glVertex2i(x,   y);
509         glTexCoord2f(texMaxX, texMinY); glVertex2i(x+w, y);
510         glTexCoord2f(texMinX, texMaxY); glVertex2i(x,   y+h);
511         glTexCoord2f(texMaxX, texMaxY); glVertex2i(x+w, y+h);
512         glEnd();
513         SDL_GL_Leave2DMode();
514 
515         /* Swap the buffers so everything is visible */
516         SDL_GL_SwapWindow(window);
517     }
518     SDL_GL_DeleteContext(context);
519     TTF_CloseFont(font);
520     cleanup(0);
521 
522     /* Not reached, but fixes compiler warnings */
523     return 0;
524 }
525 
526 #else /* HAVE_OPENGL */
527 
main(int argc,char * argv[])528 int main(int argc, char *argv[])
529 {
530     printf("No OpenGL support on this system\n");
531     return 1;
532 }
533 
534 #endif /* HAVE_OPENGL */
535 
536 /* vi: set ts=4 sw=4 expandtab: */
537