f58755d1ab3833d7a0cd9b35d9fbb1cc45c4a396
[xscreensaver] / hacks / glx / texfont.c
1 /* texfonts, Copyright (c) 2005-2012 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 #ifdef HAVE_CONFIG_H
15 # include "config.h"
16 #endif
17
18 #include <stdio.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <ctype.h>
22
23 #ifdef HAVE_COCOA
24 # ifdef USE_IPHONE
25 #  include "jwzgles.h"
26 # else
27 #  include <OpenGL/glu.h>
28 # endif
29 #else
30 # include <GL/glx.h>
31 # include <GL/glu.h>
32 #endif
33
34 #ifdef HAVE_JWZGLES
35 # include "jwzgles.h"
36 #endif /* HAVE_JWZGLES */
37
38 #include "resources.h"
39 #include "texfont.h"
40
41 /* These are in xlock-gl.c */
42 extern void clear_gl_error (void);
43 extern void check_gl_error (const char *type);
44
45 /* screenhack.h */
46 extern char *progname;
47
48 struct texture_font_data {
49   Display *dpy;
50   XFontStruct *font;
51   int cell_width, cell_height;  /* maximal charcell */
52   int tex_width, tex_height;    /* size of each texture */
53
54   int grid_mag;                 /* 1,  2,  4, or 8 */
55   int ntextures;                /* 1,  4, 16, or 64 (grid_mag ^ 2) */
56
57   GLuint texid[64];             /* must hold ntextures */
58 };
59
60
61 /* return the next larger power of 2. */
62 static int
63 to_pow2 (int i)
64 {
65   static const unsigned int pow2[] = { 
66     1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 
67     2048, 4096, 8192, 16384, 32768, 65536 };
68   int j;
69   for (j = 0; j < sizeof(pow2)/sizeof(*pow2); j++)
70     if (pow2[j] >= i) return pow2[j];
71   abort();  /* too big! */
72 }
73
74
75 /* Given a Pixmap (of screen depth), converts it to an OpenGL luminance mipmap.
76    RGB are averaged to grayscale, and the resulting value is treated as alpha.
77    Pass in the size of the pixmap; the size of the texture is returned
78    (it may be larger, since GL like powers of 2.)
79
80    We use a screen-depth pixmap instead of a 1bpp bitmap so that if the fonts
81    were drawn with antialiasing, that is preserved.
82  */
83 static void
84 bitmap_to_texture (Display *dpy, Pixmap p, Visual *visual, int *wP, int *hP)
85 {
86   Bool mipmap_p = True;
87   int ow = *wP;
88   int oh = *hP;
89   int w2 = to_pow2 (ow);
90   int h2 = to_pow2 (oh);
91   int x, y;
92   XImage *image = XGetImage (dpy, p, 0, 0, ow, oh, ~0L, ZPixmap);
93   unsigned char *data = (unsigned char *) calloc (w2 * 2, (h2 + 1));
94   unsigned char *out = data;
95
96   /* OpenGLES doesn't support GL_INTENSITY, so instead of using a
97      texture with 1 byte per pixel, the intensity value, we have
98      to use 2 bytes per pixel: solid white, and an alpha value.
99    */
100 # ifdef HAVE_JWZGLES
101 #  undef GL_INTENSITY
102 # endif
103
104 # ifdef GL_INTENSITY
105   GLuint iformat = GL_INTENSITY;
106   GLuint format  = GL_LUMINANCE;
107 # else
108   GLuint iformat = GL_LUMINANCE_ALPHA;
109   GLuint format  = GL_LUMINANCE_ALPHA;
110 # endif
111   GLuint type    = GL_UNSIGNED_BYTE;
112
113 # ifdef HAVE_JWZGLES
114   /* This would work, but it's wasteful for no benefit. */
115   mipmap_p = False;
116 # endif
117
118   for (y = 0; y < h2; y++)
119     for (x = 0; x < w2; x++) {
120       unsigned long pixel = (x >= ow || y >= oh ? 0 : XGetPixel (image, x, y));
121       /* instead of averaging all three channels, let's just use red,
122          and assume it was already grayscale. */
123       unsigned long r = pixel & visual->red_mask;
124       /* This goofy trick is to make any of RGBA/ABGR/ARGB work. */
125       pixel = ((r >> 24) | (r >> 16) | (r >> 8) | r) & 0xFF;
126 # ifndef GL_INTENSITY
127       *out++ = 0xFF;  /* 2 bytes per pixel */
128 # endif
129       *out++ = pixel;
130     }
131   XDestroyImage (image);
132   image = 0;
133
134   if (mipmap_p)
135     gluBuild2DMipmaps (GL_TEXTURE_2D, iformat, w2, h2, format, type, data);
136   else
137     glTexImage2D (GL_TEXTURE_2D, 0, iformat, w2, h2, 0, format, type, data);
138
139   {
140     char msg[100];
141     sprintf (msg, "texture font %s (%d x %d)",
142              mipmap_p ? "gluBuild2DMipmaps" : "glTexImage2D",
143              w2, h2);
144     check_gl_error (msg);
145   }
146
147
148   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
149   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
150                    mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
151
152   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
153   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
154
155   free (data);
156
157   *wP = w2;
158   *hP = h2;
159 }
160
161
162 /* Loads the font named by the X resource "res" and returns
163    a texture-font object.
164 */
165 texture_font_data *
166 load_texture_font (Display *dpy, char *res)
167 {
168   Screen *screen = DefaultScreenOfDisplay (dpy);
169   Window root = RootWindowOfScreen (screen);
170   XWindowAttributes xgwa;
171
172   texture_font_data *data = 0;
173   char *font = get_string_resource (dpy, res, "Font");
174   const char *def1 = "-*-helvetica-medium-r-normal-*-240-*";
175   const char *def2 = "-*-helvetica-medium-r-normal-*-180-*";
176   const char *def3 = "fixed";
177   XFontStruct *f;
178   int which;
179   GLint old_texture = 0;
180
181   glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture);
182
183   if (!strcmp (res, "fpsFont"))
184     def1 = "-*-courier-bold-r-normal-*-180-*";  /* Kludge. Sue me. */
185
186   XGetWindowAttributes (dpy, root, &xgwa);
187
188   if (!res || !*res) abort();
189   if (!font) font = strdup(def1);
190
191   f = XLoadQueryFont(dpy, font);
192   if (!f && !!strcmp (font, def1))
193     {
194       fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
195                progname, font, def1);
196       free (font);
197       font = strdup (def1);
198       f = XLoadQueryFont(dpy, font);
199     }
200
201   if (!f && !!strcmp (font, def2))
202     {
203       fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
204                progname, font, def2);
205       free (font);
206       font = strdup (def2);
207       f = XLoadQueryFont(dpy, font);
208     }
209
210   if (!f && !!strcmp (font, def3))
211     {
212       fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
213                progname, font, def3);
214       free (font);
215       font = strdup (def3);
216       f = XLoadQueryFont(dpy, font);
217     }
218
219   if (!f)
220     {
221       fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
222                progname, font);
223       exit (1);
224     }
225
226   free (font);
227   font = 0;
228
229   data = (texture_font_data *) calloc (1, sizeof(*data));
230   data->dpy = dpy;
231   data->font = f;
232
233   /* Figure out how many textures to use.
234      E.g., if we need 1024x1024 bits, use four 512x512 textures,
235      to be gentle to machines with low texture size limits.
236    */
237   {
238     int w = to_pow2 (16 * (f->max_bounds.rbearing - f->min_bounds.lbearing));
239     int h = to_pow2 (16 * (f->max_bounds.ascent   + f->max_bounds.descent));
240     int i = (w > h ? w : h);
241
242     if      (i <= 512)  data->grid_mag = 1;  /*  1 tex of 16x16 chars */
243     else if (i <= 1024) data->grid_mag = 2;  /*  4 tex of 8x8 chars */
244     else if (i <= 2048) data->grid_mag = 4;  /* 16 tex of 4x4 chars */
245     else                data->grid_mag = 8;  /* 32 tex of 2x2 chars */
246
247     data->ntextures = data->grid_mag * data->grid_mag;
248
249 # if 0
250     fprintf (stderr,
251              "%s: %dx%d grid of %d textures of %dx%d chars (%dx%d bits)\n",
252              progname,
253              data->grid_mag, data->grid_mag,
254              data->ntextures,
255              16 / data->grid_mag, 16 / data->grid_mag,
256              i, i);
257 # endif
258   }
259
260   for (which = 0; which < data->ntextures; which++)
261     {
262       /* Create a pixmap big enough to fit every character in the font.
263          (modulo the "ntextures" scaling.)
264          Make it square-ish, since GL likes dimensions to be powers of 2.
265        */
266       XGCValues gcv;
267       GC gc;
268       Pixmap p;
269       int cw = f->max_bounds.rbearing - f->min_bounds.lbearing;
270       int ch = f->max_bounds.ascent   + f->max_bounds.descent;
271       int grid_size = (16 / data->grid_mag);
272       int w = cw * grid_size;
273       int h = ch * grid_size;
274       int i;
275
276       data->cell_width  = cw;
277       data->cell_height = ch;
278
279       p = XCreatePixmap (dpy, root, w, h, xgwa.depth);
280       gcv.font = f->fid;
281       gcv.foreground = BlackPixelOfScreen (xgwa.screen);
282       gcv.background = BlackPixelOfScreen (xgwa.screen);
283       gc = XCreateGC (dpy, p, (GCFont|GCForeground|GCBackground), &gcv);
284       XFillRectangle (dpy, p, gc, 0, 0, w, h);
285       XSetForeground (dpy, gc, WhitePixelOfScreen (xgwa.screen));
286       for (i = 0; i < 256 / data->ntextures; i++)
287         {
288           int ii = (i + (which * 256 / data->ntextures));
289           char c = (char) ii;
290           int x = (i % grid_size) * cw;
291           int y = (i / grid_size) * ch;
292
293           /* See comment in print_texture_string for bit layout explanation.
294            */
295           int lbearing = (f->per_char && ii >= f->min_char_or_byte2
296                           ? f->per_char[ii - f->min_char_or_byte2].lbearing
297                           : f->min_bounds.lbearing);
298           int ascent   = (f->per_char && ii >= f->min_char_or_byte2
299                           ? f->per_char[ii - f->min_char_or_byte2].ascent
300                           : f->max_bounds.ascent);
301           int width    = (f->per_char && ii >= f->min_char_or_byte2
302                           ? f->per_char[ii - f->min_char_or_byte2].width
303                           : f->max_bounds.width);
304
305           if (width == 0) continue;
306           XDrawString (dpy, p, gc, x - lbearing, y + ascent, &c, 1);
307         }
308       XFreeGC (dpy, gc);
309
310       glGenTextures (1, &data->texid[which]);
311       glBindTexture (GL_TEXTURE_2D, data->texid[which]);
312       check_gl_error ("texture font load");
313       data->tex_width  = w;
314       data->tex_height = h;
315
316 #if 0  /* debugging: splat the bitmap onto the desktop root window */
317       {
318         Window win = RootWindow (dpy, 0);
319         GC gc2 = XCreateGC (dpy, win, 0, &gcv);
320         XSetForeground (dpy, gc2, BlackPixel (dpy, 0));
321         XSetBackground (dpy, gc2, WhitePixel (dpy, 0));
322         XCopyArea (dpy, p, win, gc2, 0, 0, w, h, 0, 0);
323         XFreeGC (dpy, gc2);
324         XSync(dpy, False);
325         usleep (100000);
326       }
327 #endif
328
329 #if 0  /* debugging: write the bitmap to a pgm file */
330       {
331         char file[255];
332         XImage *image;
333         int x, y;
334         FILE *f;
335         sprintf (file, "/tmp/%02d.pgm", which);
336         image = XGetImage (dpy, p, 0, 0, w, h, ~0L, ZPixmap);
337         f = fopen (file, "w");
338         fprintf (f, "P5\n%d %d\n255\n", w, h);
339         for (y = 0; y < h; y++)
340           for (x = 0; x < w; x++) {
341             unsigned long pix = XGetPixel (image, x, y);
342             unsigned long r = (pix & xgwa.visual->red_mask);
343             r = ((r >> 24) | (r >> 16) | (r >> 8) | r);
344             fprintf (f, "%c", (char) r);
345           }
346         fclose (f);
347         XDestroyImage (image);
348         fprintf (stderr, "%s: wrote %s\n", progname, file);
349       }
350 #endif
351
352       bitmap_to_texture (dpy, p, xgwa.visual, 
353                          &data->tex_width, &data->tex_height);
354       XFreePixmap (dpy, p);
355     }
356
357   /* Reset to the caller's default */
358   glBindTexture (GL_TEXTURE_2D, old_texture);
359
360   return data;
361 }
362
363
364 /* Width of the string in pixels.
365  */
366 int
367 texture_string_width (texture_font_data *data, const char *c,
368                       int *height_ret)
369 {
370   int x = 0;
371   int max_w = 0;
372   XFontStruct *f = data->font;
373   int h = f->ascent + f->descent;
374   while (*c)
375     {
376       int cc = *((unsigned char *) c);
377       if (*c == '\n')
378         {
379           if (x > max_w) max_w = x;
380           x = 0;
381           h += f->ascent + f->descent;
382         }
383       else
384         x += (f->per_char && cc >= f->min_char_or_byte2
385               ? f->per_char[cc-f->min_char_or_byte2].width
386               : f->min_bounds.rbearing);
387       c++;
388     }
389   if (x > max_w) max_w = x;
390   if (height_ret) *height_ret = h;
391
392   return max_w;
393 }
394
395
396 /* Draws the string in the scene at the current point.
397    Newlines, tab stops and subscripts are honored.
398  */
399 void
400 print_texture_string (texture_font_data *data, const char *string)
401 {
402   XFontStruct *f = data->font;
403   GLfloat line_height = f->ascent + f->descent;
404 # ifdef DO_SUBSCRIPTS
405   GLfloat sub_shift = (line_height * 0.3);
406   Bool sub_p = False;
407 # endif /* DO_SUBSCRIPTS */
408   int cw = texture_string_width (data, "m", 0);
409   int tabs = cw * 7;
410   int x, y;
411   unsigned int i;
412   GLint old_texture = 0;
413   GLfloat omatrix[16];
414   int ofront;
415
416   glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture);
417   glGetIntegerv (GL_FRONT_FACE, &ofront);
418   glGetFloatv (GL_TEXTURE_MATRIX, omatrix);
419
420   clear_gl_error ();
421
422   glPushMatrix();
423
424   glNormal3f (0, 0, 1);
425   glFrontFace (GL_CW);
426
427   glMatrixMode (GL_TEXTURE);
428   glLoadIdentity ();
429   glMatrixMode (GL_MODELVIEW);
430
431   x = 0;
432   y = 0;
433   for (i = 0; i < strlen(string); i++)
434     {
435       unsigned char c = string[i];
436       if (c == '\n')
437         {
438           y -= line_height;
439           x = 0;
440         }
441       else if (c == '\t')
442         {
443           x = ((x + tabs) / tabs) * tabs;  /* tab to tab stop */
444         }
445 # ifdef DO_SUBSCRIPTS
446       else if (c == '[' && (isdigit (string[i+1])))
447         {
448           sub_p = True;
449           y -= sub_shift;
450         }
451       else if (c == ']' && sub_p)
452         {
453           sub_p = False;
454           y += sub_shift;
455         }
456 # endif /* DO_SUBSCRIPTS */
457       else
458         {
459           /* For a small font, the texture is divided into 16x16 rectangles
460              whose size are the max_bounds charcell of the font.  Within each
461              rectangle, the individual characters' charcells sit in the upper
462              left.
463
464              For a larger font, the texture will itself be subdivided, to
465              keep the texture sizes small (in that case we deal with, e.g.,
466              4 grids of 8x8 characters instead of 1 grid of 16x16.)
467
468              Within each texture:
469
470                [A]----------------------------
471                 |     |           |   |      |
472                 |   l |         w |   | r    |
473                 |   b |         i |   | b    |
474                 |   e |         d |   | e    |
475                 |   a |         t |   | a    |
476                 |   r |         h |   | r    |
477                 |   i |           |   | i    |
478                 |   n |           |   | n    |
479                 |   g |           |   | g    |
480                 |     |           |   |      |
481                 |----[B]----------|---|      |
482                 |     |   ascent  |   |      |
483                 |     |           |   |      |
484                 |     |           |   |      |
485                 |--------------------[C]     |
486                 |         descent            |
487                 |                            | cell_width,
488                 ------------------------------ cell_height
489
490              We want to make a quad from point A to point C.
491              We want to position that quad so that point B lies at x,y.
492            */
493           int lbearing = (f->per_char && c >= f->min_char_or_byte2
494                           ? f->per_char[c - f->min_char_or_byte2].lbearing
495                           : f->min_bounds.lbearing);
496           int rbearing = (f->per_char && c >= f->min_char_or_byte2
497                           ? f->per_char[c - f->min_char_or_byte2].rbearing
498                           : f->max_bounds.rbearing);
499           int ascent   = (f->per_char && c >= f->min_char_or_byte2
500                           ? f->per_char[c - f->min_char_or_byte2].ascent
501                           : f->max_bounds.ascent);
502           int descent  = (f->per_char && c >= f->min_char_or_byte2
503                           ? f->per_char[c - f->min_char_or_byte2].descent
504                           : f->max_bounds.descent);
505           int cwidth   = (f->per_char && c >= f->min_char_or_byte2
506                           ? f->per_char[c - f->min_char_or_byte2].width
507                           : f->max_bounds.width);
508
509           unsigned char cc = c % (256 / data->ntextures);
510
511           int gs = (16 / data->grid_mag);                 /* grid size */
512
513           int ax = ((int) cc % gs) * data->cell_width;    /* point A */
514           int ay = ((int) cc / gs) * data->cell_height;
515
516           int bx = ax - lbearing;                         /* point B */
517           int by = ay + ascent;
518
519           int cx = bx + rbearing;                         /* point C */
520           int cy = by + descent;
521
522           GLfloat tax = (GLfloat) ax / data->tex_width;  /* tex coords of A */
523           GLfloat tay = (GLfloat) ay / data->tex_height;
524
525           GLfloat tcx = (GLfloat) cx / data->tex_width;  /* tex coords of C */
526           GLfloat tcy = (GLfloat) cy / data->tex_height;
527
528           GLfloat qx0 = x + lbearing;                    /* quad top left */
529           GLfloat qy0 = y + ascent;
530           GLfloat qx1 = qx0 + rbearing - lbearing;       /* quad bot right */
531           GLfloat qy1 = qy0 - (ascent + descent);
532
533           if (cwidth > 0 && c != ' ')
534             {
535               int which = c / (256 / data->ntextures);
536               if (which >= data->ntextures) abort();
537               glBindTexture (GL_TEXTURE_2D, data->texid[which]);
538
539               glBegin (GL_QUADS);
540               glTexCoord2f (tax, tay); glVertex3f (qx0, qy0, 0);
541               glTexCoord2f (tcx, tay); glVertex3f (qx1, qy0, 0);
542               glTexCoord2f (tcx, tcy); glVertex3f (qx1, qy1, 0);
543               glTexCoord2f (tax, tcy); glVertex3f (qx0, qy1, 0);
544               glEnd();
545 #if 0
546               glDisable(GL_TEXTURE_2D);
547               glBegin (GL_LINE_LOOP);
548               glTexCoord2f (tax, tay); glVertex3f (qx0, qy0, 0);
549               glTexCoord2f (tcx, tay); glVertex3f (qx1, qy0, 0);
550               glTexCoord2f (tcx, tcy); glVertex3f (qx1, qy1, 0);
551               glTexCoord2f (tax, tcy); glVertex3f (qx0, qy1, 0);
552               glEnd();
553               glEnable(GL_TEXTURE_2D);
554 #endif
555             }
556
557           x += cwidth;
558         }
559       }
560   glPopMatrix();
561
562   /* Reset to the caller's default */
563   glBindTexture (GL_TEXTURE_2D, old_texture);
564   glFrontFace (ofront);
565   
566   glMatrixMode (GL_TEXTURE);
567   glMultMatrixf (omatrix);
568   glMatrixMode (GL_MODELVIEW);
569
570   check_gl_error ("texture font print");
571 }
572
573 /* Releases the font and texture.
574  */
575 void
576 free_texture_font (texture_font_data *data)
577 {
578   int i;
579   if (data->font)
580     XFreeFont (data->dpy, data->font);
581   for (i = 0; i < data->ntextures; i++)
582     if (data->texid[i])
583       glDeleteTextures (1, &data->texid[i]);
584   free (data);
585 }