http://ftp.ksu.edu.tw/FTP/FreeBSD/distfiles/xscreensaver-4.20.tar.gz
[xscreensaver] / hacks / glx / texfont.c
1 /* texfonts, Copyright (c) 2005 Jamie Zawinski <jwz@jwz.org>
2  * Loads X11 fonts into textures for use with OpenGL.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13
14 #include "config.h"
15 #include <stdio.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <ctype.h>
19 #include <GL/glx.h>
20 #include <GL/glu.h>
21 #include "resources.h"
22 #include "texfont.h"
23
24 /* These are in xlock-gl.c */
25 extern void clear_gl_error (void);
26 extern void check_gl_error (const char *type);
27
28 /* screenhack.h */
29 extern char *progname;
30
31 struct texture_font_data {
32   Display *dpy;
33   XFontStruct *font;
34   GLuint texid;
35   int cell_width, cell_height;
36   int tex_width, tex_height;
37 };
38
39
40 /* return the next larger power of 2. */
41 static int
42 to_pow2 (int i)
43 {
44   static unsigned int pow2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
45                                  2048, 4096, 8192, 16384, 32768, 65536 };
46   int j;
47   for (j = 0; j < sizeof(pow2)/sizeof(*pow2); j++)
48     if (pow2[j] >= i) return pow2[j];
49   abort();  /* too big! */
50 }
51
52
53 /* Given a Pixmap of depth 1, converts it to an OpenGL luminance mipmap.
54    The 1 bits are drawn, the 0 bits are alpha.
55    Pass in the size of the pixmap; the size of the texture is returned
56    (it may be larger, since GL like powers of 2.)
57  */
58 static void
59 bitmap_to_texture (Display *dpy, Pixmap p, int *wP, int *hP)
60 {
61   Bool mipmap_p = True;
62   int ow = *wP;
63   int oh = *hP;
64   int w2 = to_pow2 (ow);
65   int h2 = to_pow2 (oh);
66   int x, y;
67   XImage *image = XGetImage (dpy, p, 0, 0, ow, oh, ~0L, XYPixmap);
68   unsigned char *data = (unsigned char *) calloc (w2, (h2 + 1));
69   unsigned char *out = data;
70   GLuint iformat = GL_INTENSITY;
71   GLuint format = GL_LUMINANCE;
72   GLuint type = GL_UNSIGNED_BYTE;
73
74   for (y = 0; y < h2; y++)
75     for (x = 0; x < w2; x++)
76       *out++ = (x >= ow || y >= oh ? 0 :
77                 XGetPixel (image, x, y) ? 255 : 0);
78   XDestroyImage (image);
79   image = 0;
80
81   if (mipmap_p)
82     gluBuild2DMipmaps (GL_TEXTURE_2D, iformat, w2, h2, format, type, data);
83   else
84     glTexImage2D (GL_TEXTURE_2D, 0, iformat, w2, h2, 0, format, type, data);
85
86   {
87     char msg[100];
88     sprintf (msg, "%s (%d x %d)",
89              mipmap_p ? "gluBuild2DMipmaps" : "glTexImage2D",
90              w2, h2);
91     check_gl_error (msg);
92   }
93
94
95   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
96   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
97                    mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
98
99   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
100   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
101
102   free (data);
103
104   *wP = w2;
105   *hP = h2;
106 }
107
108
109 /* Loads the font named by the X resource "res" and returns
110    a texture-font object.
111 */
112 texture_font_data *
113 load_texture_font (Display *dpy, char *res)
114 {
115   texture_font_data *data = 0;
116   const char *font = get_string_resource (res, "Font");
117   const char *def1 = "-*-times-bold-r-normal-*-240-*";
118   const char *def2 = "-*-times-bold-r-normal-*-180-*";
119   const char *def3 = "fixed";
120   XFontStruct *f;
121
122   if (!res || !*res) abort();
123   if (!font) font = def1;
124
125   f = XLoadQueryFont(dpy, font);
126   if (!f && !!strcmp (font, def1))
127     {
128       fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
129                progname, font, def1);
130       font = def1;
131       f = XLoadQueryFont(dpy, font);
132     }
133
134   if (!f && !!strcmp (font, def2))
135     {
136       fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
137                progname, font, def2);
138       font = def2;
139       f = XLoadQueryFont(dpy, font);
140     }
141
142   if (!f && !!strcmp (font, def3))
143     {
144       fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
145                progname, font, def3);
146       font = def3;
147       f = XLoadQueryFont(dpy, font);
148     }
149
150   if (!f)
151     {
152       fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
153                progname, font);
154       exit (1);
155     }
156
157   data = (texture_font_data *) calloc (1, sizeof(*data));
158   data->dpy = dpy;
159   data->font = f;
160
161   /* Create a pixmap big enough to fit every character in the font.
162      Make it square-ish, since GL likes dimensions to be powers of 2.
163    */
164   {
165     Screen *screen = DefaultScreenOfDisplay (dpy);
166     Window root = RootWindowOfScreen (screen);
167     XGCValues gcv;
168     GC gc;
169     Pixmap p;
170     int cw = f->max_bounds.rbearing - f->min_bounds.lbearing;
171     int ch = f->max_bounds.ascent   + f->max_bounds.descent;
172     int w = cw * 16;
173     int h = ch * 16;
174     int i;
175
176     data->cell_width  = cw;
177     data->cell_height = ch;
178
179     p = XCreatePixmap (dpy, root, w, h, 1);
180     gcv.font = f->fid;
181     gcv.foreground = 0;
182     gcv.background = 0;
183     gc = XCreateGC (dpy, p, (GCFont|GCForeground|GCBackground), &gcv);
184     XFillRectangle (dpy, p, gc, 0, 0, w, h);
185     XSetForeground (dpy, gc, 1);
186     for (i = 0; i < 256; i++)
187       {
188         char c = (char) i;
189         int x = (i % 16) * cw;
190         int y = (i / 16) * ch;
191
192         /* See comment in print_texture_string for bit layout explanation. */
193
194         int lbearing = (f->per_char
195                         ? f->per_char[i - f->min_char_or_byte2].lbearing
196                         : f->min_bounds.lbearing);
197         int ascent   = (f->per_char
198                         ? f->per_char[i - f->min_char_or_byte2].ascent
199                         : f->max_bounds.ascent);
200         int width    = (f->per_char
201                         ? f->per_char[i - f->min_char_or_byte2].width
202                         : f->max_bounds.width);
203
204         if (width == 0) continue;
205         XDrawString (dpy, p, gc, x - lbearing, y + ascent, &c, 1);
206       }
207     XFreeGC (dpy, gc);
208
209     glGenTextures (1, &data->texid);
210     glBindTexture (GL_TEXTURE_2D, data->texid);
211     data->tex_width  = w;
212     data->tex_height = h;
213
214 #if 0  /* debugging: splat the bitmap onto the desktop root window */
215     {
216       Window win = RootWindow (dpy, 0);
217       GC gc2 = XCreateGC (dpy, win, 0, &gcv);
218       XSetForeground (dpy, gc2, BlackPixel (dpy, 0));
219       XSetBackground (dpy, gc2, WhitePixel (dpy, 0));
220       XCopyPlane (dpy, p, win, gc2, 0, 0, w, h, 0, 0, 1);
221       XFreeGC (dpy, gc2);
222       XSync(dpy, False);
223     }
224 #endif
225
226     bitmap_to_texture (dpy, p, &data->tex_width, &data->tex_height);
227     XFreePixmap (dpy, p);
228   }
229
230   return data;
231 }
232
233
234 /* Width of the string in pixels.
235  */
236 int
237 texture_string_width (texture_font_data *data, const char *c,
238                       int *line_height_ret)
239 {
240   int w = 0;
241   XFontStruct *f = data->font;
242   while (*c)
243     {
244       int cc = *((unsigned char *) c);
245       w += (f->per_char
246             ? f->per_char[cc-f->min_char_or_byte2].width
247             : f->max_bounds.width);
248       c++;
249     }
250   if (line_height_ret)
251     *line_height_ret = f->ascent + f->descent;
252   return w;
253 }
254
255
256 /* Draws the string in the scene at the origin.
257    Newlines and tab stops are honored.
258  */
259 void
260 print_texture_string (texture_font_data *data, const char *string)
261 {
262   XFontStruct *f = data->font;
263   GLfloat line_height = f->ascent + f->descent;
264 # ifdef DO_SUBSCRIPTS
265   GLfloat sub_shift = (line_height * 0.3);
266   Bool sub_p = False;
267 # endif /* DO_SUBSCRIPTS */
268   int cw = texture_string_width (data, "m", 0);
269   int tabs = cw * 7;
270   int x, y;
271   unsigned int i;
272
273   glPushMatrix();
274
275   glBindTexture (GL_TEXTURE_2D, data->texid);
276   glNormal3f (0, 0, 1);
277
278   x = 0;
279   y = 0;
280   for (i = 0; i < strlen(string); i++)
281     {
282       char c = string[i];
283       if (c == '\n')
284         {
285           y -= line_height;
286           x = 0;
287         }
288       else if (c == '\t')
289         {
290           x = ((x + tabs) / tabs) * tabs;  /* tab to tab stop */
291         }
292 # ifdef DO_SUBSCRIPTS
293       else if (c == '[' && (isdigit (string[i+1])))
294         {
295           sub_p = True;
296           y -= sub_shift;
297         }
298       else if (c == ']' && sub_p)
299         {
300           sub_p = False;
301           y += sub_shift;
302         }
303 # endif /* DO_SUBSCRIPTS */
304       else
305         {
306           /* The texture is divided into 16x16 rectangles whose size are
307              the max_bounds charcell of the font.  Within each rectangle,
308              the individual characters' charcells sit in the upper left.
309
310                [A]----------------------------
311                 |     |           |   |      |
312                 |   l |         w |   | r    |
313                 |   b |         i |   | b    |
314                 |   e |         d |   | e    |
315                 |   a |         t |   | a    |
316                 |   r |         h |   | r    |
317                 |   i |           |   | i    |
318                 |   n |           |   | n    |
319                 |   g |           |   | g    |
320                 |     |           |   |      |
321                 |----[B]----------|---|      |
322                 |     |   ascent  |   |      |
323                 |     |           |   |      |
324                 |     |           |   |      |
325                 |--------------------[C]     |
326                 |         descent            |
327                 |                            | cell_width,
328                 ------------------------------ cell_height
329
330              We want to make a quad from point A to point C.
331              We want to position that quad so that point B lies at x,y.
332            */
333           int lbearing = (f->per_char
334                           ? f->per_char[c - f->min_char_or_byte2].lbearing
335                           : f->min_bounds.lbearing);
336           int rbearing = (f->per_char
337                           ? f->per_char[c - f->min_char_or_byte2].rbearing
338                           : f->max_bounds.rbearing);
339           int ascent   = (f->per_char
340                           ? f->per_char[c - f->min_char_or_byte2].ascent
341                           : f->max_bounds.ascent);
342           int descent  = (f->per_char
343                           ? f->per_char[c - f->min_char_or_byte2].descent
344                           : f->max_bounds.descent);
345           int cwidth   = (f->per_char
346                           ? f->per_char[c - f->min_char_or_byte2].width
347                           : f->max_bounds.width);
348
349           int ax = ((int) c % 16) * data->cell_width;     /* point A */
350           int ay = ((int) c / 16) * data->cell_height;
351
352           int bx = ax - lbearing;                         /* point B */
353           int by = ay + ascent;
354
355           int cx = bx + rbearing;                         /* point C */
356           int cy = by + descent;
357
358           GLfloat tax = (GLfloat) ax / data->tex_width;  /* tex coords of A */
359           GLfloat tay = (GLfloat) ay / data->tex_height;
360
361           GLfloat tcx = (GLfloat) cx / data->tex_width;  /* tex coords of C */
362           GLfloat tcy = (GLfloat) cy / data->tex_height;
363
364           GLfloat qx0 = x + lbearing;                    /* quad top left */
365           GLfloat qy0 = y + ascent;
366           GLfloat qx1 = qx0 + rbearing - lbearing;       /* quad bot right */
367           GLfloat qy1 = qy0 - (ascent + descent);
368
369           if (cwidth > 0 && c != ' ')
370             {
371               glBegin (GL_QUADS);
372               glTexCoord2f (tax, tay); glVertex3f (qx0, qy0, 0);
373               glTexCoord2f (tcx, tay); glVertex3f (qx1, qy0, 0);
374               glTexCoord2f (tcx, tcy); glVertex3f (qx1, qy1, 0);
375               glTexCoord2f (tax, tcy); glVertex3f (qx0, qy1, 0);
376               glEnd();
377 #if 0
378               glDisable(GL_TEXTURE_2D);
379               glBegin (GL_LINE_LOOP);
380               glTexCoord2f (tax, tay); glVertex3f (qx0, qy0, 0);
381               glTexCoord2f (tcx, tay); glVertex3f (qx1, qy0, 0);
382               glTexCoord2f (tcx, tcy); glVertex3f (qx1, qy1, 0);
383               glTexCoord2f (tax, tcy); glVertex3f (qx0, qy1, 0);
384               glEnd();
385               glEnable(GL_TEXTURE_2D);
386 #endif
387             }
388
389           x += cwidth;
390         }
391       }
392   glPopMatrix();
393 }
394
395 /* Releases the font and texture.
396  */
397 void
398 free_texture_font (texture_font_data *data)
399 {
400   if (data->font)
401     XFreeFont (data->dpy, data->font);
402   if (data->texid)
403     glDeleteTextures (1, &data->texid);
404   free (data);
405 }