From http://www.jwz.org/xscreensaver/xscreensaver-5.33.tar.gz
[xscreensaver] / hacks / glx / texfont.c
1 /* texfonts, Copyright (c) 2005-2015 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  *
11  * Renders X11 fonts into textures for use with OpenGL.
12  * A higher level API is in glxfonts.c.
13  */
14
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <ctype.h>
23
24 #ifdef HAVE_COCOA
25 # ifdef USE_IPHONE
26 #  include "jwzgles.h"
27 # else
28 #  include <OpenGL/glu.h>
29 # endif
30 #elif defined(HAVE_ANDROID)
31 # include <GLES/gl.h>
32 # include "jwzgles.h"
33 #else
34 # include <GL/glx.h>
35 # include <GL/glu.h>
36 #endif
37
38 #ifdef HAVE_JWZGLES
39 # include "jwzgles.h"
40 #endif /* HAVE_JWZGLES */
41
42 #ifdef HAVE_XSHM_EXTENSION
43 # include "xshm.h"
44 #endif /* HAVE_XSHM_EXTENSION */
45
46 #include "xft.h"
47 #include "resources.h"
48 #include "texfont.h"
49 #include "fps.h"        /* for current_device_rotation() */
50
51 #undef HAVE_XSHM_EXTENSION  /* doesn't actually do any good here */
52
53
54 /* These are in xlock-gl.c */
55 extern void clear_gl_error (void);
56 extern void check_gl_error (const char *type);
57
58 /* screenhack.h */
59 extern char *progname;
60
61 /* LRU cache of textures, to optimize the case where we're drawing the
62    same strings repeatedly.
63  */
64 typedef struct texfont_cache texfont_cache;
65 struct texfont_cache {
66   char *string;
67   GLuint texid;
68   XCharStruct extents;
69   int tex_width, tex_height;
70   texfont_cache *next;
71 };
72
73 struct texture_font_data {
74   Display *dpy;
75   XftFont *xftfont;
76   int cache_size;
77   texfont_cache *cache;
78 };
79
80
81 #undef countof
82 #define countof(x) (sizeof((x))/sizeof((*x)))
83
84
85 /* return the next larger power of 2. */
86 static int
87 to_pow2 (int i)
88 {
89   static const unsigned int pow2[] = { 
90     1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 
91     2048, 4096, 8192, 16384, 32768, 65536 };
92   int j;
93   for (j = 0; j < sizeof(pow2)/sizeof(*pow2); j++)
94     if (pow2[j] >= i) return pow2[j];
95   abort();  /* too big! */
96 }
97
98
99 /* Given a Pixmap (of screen depth), converts it to an OpenGL luminance mipmap.
100    RGB are averaged to grayscale, and the resulting value is treated as alpha.
101    Pass in the size of the pixmap; the size of the texture is returned
102    (it may be larger, since GL like powers of 2.)
103
104    We use a screen-depth pixmap instead of a 1bpp bitmap so that if the fonts
105    were drawn with antialiasing, that is preserved.
106  */
107 static void
108 bitmap_to_texture (Display *dpy, Pixmap p, Visual *visual, int depth,
109                    int *wP, int *hP)
110 {
111   Bool mipmap_p = True;
112   int ow = *wP;
113   int oh = *hP;
114   int w2 = to_pow2 (ow);
115   int h2 = to_pow2 (oh);
116   int x, y, max, scale;
117   XImage *image = 0;
118   unsigned char *data = (unsigned char *) calloc (w2 * 2, (h2 + 1));
119   unsigned char *out = data;
120
121   /* If either dimension is larger than the supported size, reduce.
122      We still return the old size to keep the caller's math working,
123      but the texture itself will have fewer pixels in it.
124    */
125   glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max);
126   scale = 1;
127   while (w2 > max || h2 > max)
128     {
129       w2 /= 2;
130       h2 /= 2;
131       scale *= 2;
132     }
133
134   /* OpenGLES doesn't support GL_INTENSITY, so instead of using a
135      texture with 1 byte per pixel, the intensity value, we have
136      to use 2 bytes per pixel: solid white, and an alpha value.
137    */
138 # ifdef HAVE_JWZGLES
139 #  undef GL_INTENSITY
140 # endif
141
142 # ifdef GL_INTENSITY
143   GLuint iformat = GL_INTENSITY;
144   GLuint format  = GL_LUMINANCE;
145 # else
146   GLuint iformat = GL_LUMINANCE_ALPHA;
147   GLuint format  = GL_LUMINANCE_ALPHA;
148 # endif
149   GLuint type    = GL_UNSIGNED_BYTE;
150
151 # ifdef HAVE_XSHM_EXTENSION
152   Bool use_shm = get_boolean_resource (dpy, "useSHM", "Boolean");
153   XShmSegmentInfo shm_info;
154 # endif /* HAVE_XSHM_EXTENSION */
155
156 # ifdef HAVE_XSHM_EXTENSION
157   if (use_shm)
158     {
159       image = create_xshm_image (dpy, visual, depth, ZPixmap, 0, &shm_info,
160                                  ow, oh);
161       if (image)
162         XShmGetImage (dpy, p, image, 0, 0, ~0L);
163       else
164         use_shm = False;
165     }
166 # endif /* HAVE_XSHM_EXTENSION */
167
168   if (!image)
169     image = XGetImage (dpy, p, 0, 0, ow, oh, ~0L, ZPixmap);
170
171 # ifdef HAVE_JWZGLES
172   /* This would work, but it's wasteful for no benefit. */
173   mipmap_p = False;
174 # endif
175
176 # ifdef DUMP_BITMAPS
177   fprintf (stderr, "\n");
178 # endif
179   for (y = 0; y < h2; y++) {
180     for (x = 0; x < w2; x++) {
181       /* Might be better to average a scale x scale square of source pixels,
182          but at the resolutions we're dealing with, this is probably good
183          enough. */
184       int sx = x * scale;
185       int sy = y * scale;
186       unsigned long pixel = (sx >= ow || sy >= oh ? 0 :
187                              XGetPixel (image, sx, sy));
188       /* instead of averaging all three channels, let's just use red,
189          and assume it was already grayscale. */
190       unsigned long r = pixel & visual->red_mask;
191       /* This goofy trick is to make any of RGBA/ABGR/ARGB work. */
192       pixel = ((r >> 24) | (r >> 16) | (r >> 8) | r) & 0xFF;
193
194 # ifdef DUMP_BITMAPS
195       if (sx < ow && sy < oh)
196 #  ifdef HAVE_COCOA
197         fprintf (stderr, "%c", 
198                  r >= 0xFF000000 ? '#' : 
199                  r >= 0x88000000 ? '%' : 
200                  r ? '.' : ' ');
201 #  else
202         fprintf (stderr, "%c", 
203                  r >= 0xFF0000 ? '#' : 
204                  r >= 0x880000 ? '%' : 
205                  r ? '.' : ' ');
206 #  endif
207 # endif
208
209 # if 0  /* Debugging checkerboard */
210       if (sx < ow && sy < oh && (((sx / 4) & 1) ^ ((sy / 4) & 1)))
211         pixel = 0x3F;
212 # endif
213
214 # ifndef GL_INTENSITY
215       *out++ = 0xFF;  /* 2 bytes per pixel (luminance, alpha) */
216 # endif
217       *out++ = pixel;
218     }
219 # ifdef DUMP_BITMAPS
220     fprintf (stderr, "\n");
221 # endif
222   }
223
224 # ifdef HAVE_XSHM_EXTENSION
225   if (use_shm)
226     destroy_xshm_image (dpy, image, &shm_info);
227   else
228 # endif /* HAVE_XSHM_EXTENSION */
229     XDestroyImage (image);
230
231   image = 0;
232
233   if (mipmap_p)
234     gluBuild2DMipmaps (GL_TEXTURE_2D, iformat, w2, h2, format, type, data);
235   else
236     glTexImage2D (GL_TEXTURE_2D, 0, iformat, w2, h2, 0, format, type, data);
237
238   {
239     char msg[100];
240     sprintf (msg, "texture font %s (%d x %d)",
241              mipmap_p ? "gluBuild2DMipmaps" : "glTexImage2D",
242              w2, h2);
243     check_gl_error (msg);
244   }
245
246   free (data);
247
248   *wP = w2 * scale;
249   *hP = h2 * scale;
250 }
251
252
253 /* Loads the font named by the X resource "res" and returns
254    a texture-font object.
255 */
256 texture_font_data *
257 load_texture_font (Display *dpy, char *res)
258 {
259   int screen = DefaultScreen (dpy);
260   char *font = get_string_resource (dpy, res, "Font");
261   const char *def1 = "-*-helvetica-medium-r-normal-*-*-180-*-*-*-*-*-*";
262   const char *def2 = "-*-helvetica-medium-r-normal-*-*-140-*-*-*-*-*-*";
263   const char *def3 = "fixed";
264   XftFont *f = 0;
265   texture_font_data *data;
266   int cache_size = get_integer_resource (dpy, "texFontCacheSize", "Integer");
267
268   /* Hacks that draw a lot of different strings on the screen simultaneously,
269      like Star Wars, should set this to a larger value for performance. */
270   if (cache_size <= 0)
271     cache_size = 30;
272
273   if (!res || !*res) abort();
274
275   if (!strcmp (res, "fpsFont")) {  /* Kludge. */
276     def1 = "-*-courier-bold-r-normal-*-*-140-*-*-*-*-*-*";
277     cache_size = 0;  /* No need for a cache on FPS: already throttled. */
278   }
279
280   if (!font) font = strdup(def1);
281
282   f = XftFontOpenXlfd (dpy, screen, font);
283   if (!f && !!strcmp (font, def1))
284     {
285       fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
286                progname, font, def1);
287       free (font);
288       font = strdup (def1);
289       f = XftFontOpenXlfd (dpy, screen, font);
290     }
291
292   if (!f && !!strcmp (font, def2))
293     {
294       fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
295                progname, font, def2);
296       free (font);
297       font = strdup (def2);
298       f = XftFontOpenXlfd (dpy, screen, font);
299     }
300
301   if (!f && !!strcmp (font, def3))
302     {
303       fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
304                progname, font, def3);
305       free (font);
306       font = strdup (def3);
307       f = XftFontOpenXlfd (dpy, screen, font);
308     }
309
310   if (!f)
311     {
312       fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
313                progname, font);
314       abort();
315     }
316
317   free (font);
318   font = 0;
319
320   data = (texture_font_data *) calloc (1, sizeof(*data));
321   data->dpy = dpy;
322   data->xftfont = f;
323   data->cache_size = cache_size;
324
325   return data;
326 }
327
328
329 /* Measure the string, returning the overall metrics.
330    Newlines and tab stops are honored.
331    Any numbers inside [] will be rendered as a subscript.
332
333    The origin is at the origin of the first character, so subsequent
334    lines of a multi-line string look like descenders (below baseline).
335
336    If an XftDraw is supplied, render the string as well, at X,Y.
337    Positive Y is down (X11 style, not OpenGL style).
338  */
339 static void
340 iterate_texture_string (texture_font_data *data,
341                         const char *s,
342                         int draw_x, int draw_y,
343                         XftDraw *xftdraw, XftColor *xftcolor,
344                         XCharStruct *metrics_ret)
345 {
346   int line_height = data->xftfont->ascent + data->xftfont->descent;
347   int subscript_offset = line_height * 0.3;
348   const char *os = s;
349   Bool sub_p = False, osub_p = False;
350   int cw = 0, tabs = 0;
351   XCharStruct overall = { 0, };
352   int x  = 0, y  = 0;
353   int ox = x, oy = y;
354
355   while (1)
356     {
357       if (*s == 0 ||
358           *s == '\n' ||
359           *s == '\t' ||
360           (*s == '[' && isdigit(s[1])) ||
361           (*s == ']' && sub_p))
362         {
363           if (s != os)
364             {
365               XGlyphInfo e;
366               XCharStruct c;
367               int y2 = y;
368               if (sub_p) y2 += subscript_offset;
369
370               XftTextExtentsUtf8 (data->dpy, data->xftfont,
371                                   (FcChar8 *) os, (int) (s - os),
372                                   &e);
373               c.lbearing = -e.x;                /* XGlyphInfo to XCharStruct */
374               c.rbearing =  e.width  - e.x;
375               c.ascent   =  e.y;
376               c.descent  =  e.height - e.y;
377               c.width    =  e.xOff;
378
379 # undef MAX
380 # undef MIN
381 # define MAX(A,B) ((A)>(B)?(A):(B))
382 # define MIN(A,B) ((A)<(B)?(A):(B))
383               overall.ascent   = MAX (overall.ascent,   -y2 + c.ascent);
384               overall.descent  = MAX (overall.descent,   y2 + c.descent);
385               overall.lbearing = MIN (overall.lbearing, (x  + c.lbearing));
386               overall.rbearing = MAX (overall.rbearing,  x  + c.rbearing);
387               overall.width    = MAX (overall.width,     x  + c.width);
388               x += c.width;
389 # undef MAX
390 # undef MIN
391             }
392
393           if (*s == '\n')
394             {
395               x = 0;
396               y += line_height;
397               sub_p = False;
398             }
399           else if (*s == '\t')
400             {
401               if (! cw)
402                 {
403                   /* Measure "m" to determine tab width. */
404                   XGlyphInfo e;
405                   XftTextExtentsUtf8 (data->dpy, data->xftfont,
406                                       (FcChar8 *) "m", 1, &e);
407                   cw = e.xOff;
408                   if (cw <= 0) cw = 1;
409                   tabs = cw * 7;
410                 }
411               x = ((x + tabs) / tabs) * tabs;
412             }
413           else if (*s == '[' && isdigit(s[1]))
414             sub_p = True;
415           else if (*s == ']' && sub_p)
416             sub_p = False;
417
418           if (xftdraw && s != os)
419             XftDrawStringUtf8 (xftdraw, xftcolor, data->xftfont,
420                                draw_x + ox,
421                                draw_y +
422                                oy + (osub_p ? subscript_offset : 0),
423                                (FcChar8 *) os, (int) (s - os));
424           if (!*s) break;
425           os = s+1;
426           ox = x;
427           oy = y;
428           osub_p = sub_p;
429         }
430       s++;
431     }
432
433   if (metrics_ret)
434     *metrics_ret = overall;
435 }
436
437
438 /* Bounding box of the multi-line string, in pixels,
439    and overall ascent/descent of the font.
440  */
441 void
442 texture_string_metrics (texture_font_data *data, const char *s,
443                         XCharStruct *metrics_ret,
444                         int *ascent_ret, int *descent_ret)
445 {
446   if (metrics_ret)
447     iterate_texture_string (data, s, 0, 0, 0, 0, metrics_ret);
448   if (ascent_ret)  *ascent_ret  = data->xftfont->ascent;
449   if (descent_ret) *descent_ret = data->xftfont->descent;
450 }
451
452
453 /* Returns a cache entry for this string, with a valid texid.
454    If the returned entry has a string in it, the texture is valid.
455    Otherwise it is an empty entry waiting to be rendered.
456  */
457 static struct texfont_cache *
458 get_cache (texture_font_data *data, const char *string)
459 {
460   int count = 0;
461   texfont_cache *prev = 0, *prev2 = 0, *curr = 0, *next = 0;
462
463   if (data->cache)
464     for (prev2 = 0, prev = 0, curr = data->cache, next = curr->next;
465          curr;
466          prev2 = prev, prev = curr, curr = next,
467            next = (curr ? curr->next : 0), count++)
468       {
469         if (!strcmp (string, curr->string))
470           {
471             if (prev)
472               prev->next = next;       /* Unlink from list */
473             if (curr != data->cache)
474               {
475                 curr->next = data->cache;  /* Move to front */
476                 data->cache = curr;
477               }
478             return curr;
479           }
480       }
481
482   /* Made it to the end of the list without a hit.
483      If the cache is full, empty out the last one on the list,
484      and move it to the front.  Keep the texid.
485    */
486   if (count > data->cache_size)
487     {
488       free (prev->string);
489       prev->string     = 0;
490       prev->tex_width  = 0;
491       prev->tex_height = 0;
492       memset (&prev->extents, 0, sizeof(prev->extents));
493       if (prev2)
494         prev2->next = 0;
495       if (prev != data->cache)
496         prev->next = data->cache;
497       data->cache = prev;
498       return prev;
499     }
500
501   /* Not cached, and cache not full.  Add a new entry at the front,
502      and allocate a new texture for it.
503    */
504   curr = (struct texfont_cache *) calloc (1, sizeof(*prev));
505   glGenTextures (1, &curr->texid);
506   curr->string = 0;
507   curr->next = data->cache;
508   data->cache = curr;
509
510   return curr;
511 }
512
513
514 /* Renders the given string into the prevailing texture.
515    Returns the metrics of the text, and size of the texture.
516  */
517 void
518 string_to_texture (texture_font_data *data, const char *string,
519                    XCharStruct *extents_ret,
520                    int *tex_width_ret, int *tex_height_ret)
521 {
522   Window window = RootWindow (data->dpy, 0);
523   Pixmap p;
524   XGCValues gcv;
525   GC gc;
526   XWindowAttributes xgwa;
527   XRenderColor rcolor;
528   XftColor xftcolor;
529   XftDraw *xftdraw;
530   int width, height;
531   XCharStruct overall;
532
533   /* Measure the string and create a Pixmap of the proper size.
534    */
535   XGetWindowAttributes (data->dpy, window, &xgwa);
536   iterate_texture_string (data, string, 0, 0, 0, 0, &overall);
537   width  = overall.rbearing - overall.lbearing;
538   height = overall.ascent   + overall.descent;
539   if (width  <= 0) width  = 1;
540   if (height <= 0) height = 1;
541   p = XCreatePixmap (data->dpy, window, width, height, xgwa.depth);
542
543   gcv.foreground = BlackPixelOfScreen (xgwa.screen);
544   gc = XCreateGC (data->dpy, p, GCForeground, &gcv);
545   XFillRectangle (data->dpy, p, gc, 0, 0, width, height);
546   XFreeGC (data->dpy, gc);
547
548   /* Render the string into the pixmap.
549    */
550   rcolor.red = rcolor.green = rcolor.blue = rcolor.alpha = 0xFFFF;
551   XftColorAllocValue (data->dpy, xgwa.visual, xgwa.colormap,
552                       &rcolor, &xftcolor);
553   xftdraw = XftDrawCreate (data->dpy, p, xgwa.visual, xgwa.colormap);
554   iterate_texture_string (data, string,
555                           -overall.lbearing, overall.ascent,
556                           xftdraw, &xftcolor, 0);
557   XftDrawDestroy (xftdraw);
558   XftColorFree (data->dpy, xgwa.visual, xgwa.colormap, &xftcolor);
559
560   /* Copy the bits from the Pixmap into a texture, unless it's cached.
561    */
562   bitmap_to_texture (data->dpy, p, xgwa.visual, xgwa.depth, 
563                      &width, &height);
564   XFreePixmap (data->dpy, p);
565
566   if (extents_ret)    *extents_ret    = overall;
567   if (tex_width_ret)  *tex_width_ret  = width;
568   if (tex_height_ret) *tex_height_ret = height;
569 }
570
571
572 /* Set the various OpenGL parameters for properly rendering things
573    with a texture generated by string_to_texture().
574  */
575 void
576 enable_texture_string_parameters (void)
577 {
578   glEnable (GL_TEXTURE_2D);
579
580   /* Texture-rendering parameters to make font pixmaps tolerable to look at.
581    */
582   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
583   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
584                    GL_LINEAR_MIPMAP_LINEAR);
585
586   /* LOD bias is part of OpenGL 1.4.
587      GL_EXT_texture_lod_bias has been present since the original iPhone.
588   */
589 # if !defined(GL_TEXTURE_LOD_BIAS) && defined(GL_TEXTURE_LOD_BIAS_EXT)
590 #   define GL_TEXTURE_LOD_BIAS GL_TEXTURE_LOD_BIAS_EXT
591 # endif
592 # ifdef GL_TEXTURE_LOD_BIAS
593   glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.25);
594 # endif
595   clear_gl_error();  /* invalid enum on iPad 3 */
596
597   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
598   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
599
600   /* Don't write the transparent parts of the quad into the depth buffer. */
601   glAlphaFunc (GL_GREATER, 0.01);
602   glEnable (GL_ALPHA_TEST);
603   glEnable (GL_BLEND);
604   glDisable (GL_LIGHTING);
605   glDisable (GL_TEXTURE_GEN_S);
606   glDisable (GL_TEXTURE_GEN_T);
607   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
608 }
609
610
611 /* Draws the string in the scene at the origin.
612    Newlines and tab stops are honored.
613    Any numbers inside [] will be rendered as a subscript.
614    Assumes the font has been loaded as with load_texture_font().
615
616    The origin is at the origin of the first character, so subsequent
617    lines of a multi-line string are below that.
618  */
619 void
620 print_texture_string (texture_font_data *data, const char *string)
621 {
622   XCharStruct overall;
623   int tex_width, tex_height;
624   texfont_cache *cache;
625   GLint old_texture;
626
627   if (!*string) return;
628
629   clear_gl_error ();
630
631   /* Save the prevailing texture ID, and bind ours.  Restored at the end. */
632   glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture);
633
634   cache = get_cache (data, string);
635
636   glBindTexture (GL_TEXTURE_2D, cache->texid);
637   check_gl_error ("texture font binding");
638
639   /* Measure the string and make a pixmap that will fit it,
640      unless it's cached.
641    */
642   if (cache->string)
643     {
644       overall    = data->cache->extents;
645       tex_width  = data->cache->tex_width;
646       tex_height = data->cache->tex_height;
647     }
648   else
649     string_to_texture (data, string, &overall, &tex_width, &tex_height);
650
651   {
652     int ofront, oblend;
653     Bool alpha_p, blend_p, light_p, gen_s_p, gen_t_p;
654     GLfloat omatrix[16];
655     GLfloat qx0, qy0, qx1, qy1;
656     GLfloat tx0, ty0, tx1, ty1;
657
658     /* Save the prevailing texture environment, and set up ours.
659      */
660     glGetIntegerv (GL_FRONT_FACE, &ofront);
661     glGetIntegerv (GL_BLEND_DST, &oblend);
662     glGetFloatv (GL_TEXTURE_MATRIX, omatrix);
663     blend_p = glIsEnabled (GL_BLEND);
664     alpha_p = glIsEnabled (GL_ALPHA_TEST);
665     light_p = glIsEnabled (GL_LIGHTING);
666     gen_s_p = glIsEnabled (GL_TEXTURE_GEN_S);
667     gen_t_p = glIsEnabled (GL_TEXTURE_GEN_T);
668
669     glPushMatrix();
670
671     glNormal3f (0, 0, 1);
672     glFrontFace (GL_CW);
673
674     glMatrixMode (GL_TEXTURE);
675     glLoadIdentity ();
676     glMatrixMode (GL_MODELVIEW);
677
678     enable_texture_string_parameters();
679
680     /* Draw a quad with that texture on it, possibly using a cached texture.
681        Position the XCharStruct origin at 0,0 in the scene.
682      */
683     qx0 =  overall.lbearing;
684     qy0 = -overall.descent;
685     qx1 =  overall.rbearing;
686     qy1 =  overall.ascent;
687
688     tx0 = 0;
689     ty1 = 0;
690     tx1 = (overall.rbearing - overall.lbearing) / (GLfloat) tex_width;
691     ty0 = (overall.ascent + overall.descent)    / (GLfloat) tex_height;
692
693     glFrontFace (GL_CCW);
694     glBegin (GL_QUADS);
695     glTexCoord2f (tx0, ty0); glVertex3f (qx0, qy0, 0);
696     glTexCoord2f (tx1, ty0); glVertex3f (qx1, qy0, 0);
697     glTexCoord2f (tx1, ty1); glVertex3f (qx1, qy1, 0);
698     glTexCoord2f (tx0, ty1); glVertex3f (qx0, qy1, 0);
699     glEnd();
700
701     glPopMatrix();
702
703     /* Reset to the caller's texture environment.
704      */
705     glBindTexture (GL_TEXTURE_2D, old_texture);
706     glFrontFace (ofront);
707     if (!alpha_p) glDisable (GL_ALPHA_TEST);
708     if (!blend_p) glDisable (GL_BLEND);
709     if (light_p) glEnable (GL_LIGHTING);
710     if (gen_s_p) glEnable (GL_TEXTURE_GEN_S);
711     if (gen_t_p) glEnable (GL_TEXTURE_GEN_T);
712
713     glBlendFunc (GL_SRC_ALPHA, oblend);
714   
715     glMatrixMode (GL_TEXTURE);
716     glMultMatrixf (omatrix);
717     glMatrixMode (GL_MODELVIEW);
718
719     check_gl_error ("texture font print");
720
721     /* Store this string into the cache, unless that's where it came from.
722      */
723     if (!cache->string)
724       {
725         cache->string     = strdup (string);
726         cache->extents    = overall;
727         cache->tex_width  = tex_width;
728         cache->tex_height = tex_height;
729       }
730   }
731 }
732
733
734 /* Draws the string on the window at the given pixel position.
735    Newlines and tab stops are honored.
736    Any numbers inside [] will be rendered as a subscript.
737    Assumes the font has been loaded as with load_texture_font().
738
739    Position is 0 for center, 1 for top left, 2 for bottom left.
740  */
741 void
742 print_texture_label (Display *dpy,
743                      texture_font_data *data,
744                      int window_width, int window_height,
745                      int position,
746                      const char *string)
747 {
748   GLfloat color[4];
749
750   Bool tex_p   = glIsEnabled (GL_TEXTURE_2D);
751   Bool texs_p  = glIsEnabled (GL_TEXTURE_GEN_S);
752   Bool text_p  = glIsEnabled (GL_TEXTURE_GEN_T);
753   Bool depth_p = glIsEnabled (GL_DEPTH_TEST);
754   Bool cull_p  = glIsEnabled (GL_CULL_FACE);
755   Bool fog_p   = glIsEnabled (GL_FOG);
756   GLint ovp[4];
757
758 #  ifndef HAVE_JWZGLES
759   GLint opoly[2];
760   glGetIntegerv (GL_POLYGON_MODE, opoly);
761 #  endif
762
763   glGetIntegerv (GL_VIEWPORT, ovp);
764
765   glGetFloatv (GL_CURRENT_COLOR, color);
766
767   glEnable (GL_TEXTURE_2D);
768   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
769   glPolygonMode (GL_FRONT, GL_FILL);
770
771   glDisable (GL_TEXTURE_GEN_S);
772   glDisable (GL_TEXTURE_GEN_T);
773   glDisable (GL_CULL_FACE);
774   glDisable (GL_FOG);
775
776   glDisable (GL_DEPTH_TEST);
777
778   /* Each matrix mode has its own stack, so we need to push/pop
779      them separately.
780    */
781   glMatrixMode(GL_PROJECTION);
782   glPushMatrix();
783   {
784     glLoadIdentity();
785
786     glMatrixMode(GL_MODELVIEW);
787     glPushMatrix();
788     {
789       XCharStruct cs;
790       int ascent, descent;
791       int x, y, w, h, swap;
792       int rot = (int) current_device_rotation();
793
794       glLoadIdentity();
795       glViewport (0, 0, window_width, window_height);
796       glOrtho (0, window_width, 0, window_height, -1, 1);
797
798       while (rot <= -180) rot += 360;
799       while (rot >   180) rot -= 360;
800
801       texture_string_metrics (data, string, &cs, &ascent, &descent);
802       h = cs.ascent + cs.descent;
803       w  = cs.width;
804
805 # ifdef USE_IPHONE
806       {
807         /* Size of the font is in points, so scale iOS pixels to points. */
808         GLfloat scale = window_width / 768.0;
809         if (scale < 1) scale = 1;
810
811         /* jwxyz-XLoadFont has already doubled the font size, to compensate
812            for physically smaller screens.  Undo that, since OpenGL hacks
813            use full-resolution framebuffers, unlike X11 hacks. */
814         scale /= 2;
815
816         window_width  /= scale;
817         window_height /= scale;
818         glScalef (scale, scale, scale);
819       }
820 # endif /* USE_IPHONE */
821
822       if (rot > 135 || rot < -135)              /* 180 */
823         {
824           glTranslatef (window_width, window_height, 0);
825           glRotatef (180, 0, 0, 1);
826         }
827       else if (rot > 45)                        /* 90 */
828         {
829           glTranslatef (window_width, 0, 0);
830           glRotatef (90, 0, 0, 1);
831           swap = window_width;
832           window_width = window_height;
833           window_height = swap;
834         }
835       else if (rot < -45)                       /* 270 */
836         {
837           glTranslatef(0, window_height, 0);
838           glRotatef (-90, 0, 0, 1);
839           swap = window_width;
840           window_width = window_height;
841           window_height = swap;
842         }
843
844       switch (position) {
845       case 0:                                   /* center */
846         x = (window_width  - w) / 2;
847         y = (window_height + h) / 2 - ascent;
848         break;
849       case 1:                                   /* top */
850         x = ascent;
851         y = window_height - ascent*2;
852         break;
853       case 2:                                   /* bottom */
854         x = ascent;
855         y = h;
856         break;
857       default:
858         abort();
859       }
860
861       glTranslatef (x, y, 0);
862
863       /* draw the text five times, to give it a border. */
864       {
865         const XPoint offsets[] = {{ -1, -1 },
866                                   { -1,  1 },
867                                   {  1,  1 },
868                                   {  1, -1 },
869                                   {  0,  0 }};
870         int i;
871
872         glColor3f (0, 0, 0);
873         for (i = 0; i < countof(offsets); i++)
874           {
875             if (offsets[i].x == 0)
876               glColor4fv (color);
877             glPushMatrix();
878             glTranslatef (offsets[i].x, offsets[i].y, 0);
879             print_texture_string (data, string);
880             glPopMatrix();
881           }
882       }
883     }
884     glPopMatrix();
885   }
886   glMatrixMode(GL_PROJECTION);
887   glPopMatrix();
888
889   if (tex_p)   glEnable (GL_TEXTURE_2D); else glDisable (GL_TEXTURE_2D);
890   if (texs_p)  glEnable (GL_TEXTURE_GEN_S);/*else glDisable(GL_TEXTURE_GEN_S);*/
891   if (text_p)  glEnable (GL_TEXTURE_GEN_T);/*else glDisable(GL_TEXTURE_GEN_T);*/
892   if (depth_p) glEnable (GL_DEPTH_TEST); else glDisable (GL_DEPTH_TEST);
893   if (cull_p)  glEnable (GL_CULL_FACE); /*else glDisable (GL_CULL_FACE);*/
894   if (fog_p)   glEnable (GL_FOG); /*else glDisable (GL_FOG);*/
895
896   glViewport (ovp[0], ovp[1], ovp[2], ovp[3]);
897
898 # ifndef HAVE_JWZGLES
899   glPolygonMode (GL_FRONT, opoly[0]);
900 # endif
901
902   glMatrixMode(GL_MODELVIEW);
903 }
904
905
906 /* Releases the font and texture.
907  */
908 void
909 free_texture_font (texture_font_data *data)
910 {
911   while (data->cache)
912     {
913       texfont_cache *next = data->cache->next;
914       glDeleteTextures (1, &data->cache->texid);
915       free (data->cache);
916       data->cache = next;
917     }
918   if (data->xftfont)
919     XftFontClose (data->dpy, data->xftfont);
920   free (data);
921 }