From http://www.jwz.org/xscreensaver/xscreensaver-5.39.tar.gz
[xscreensaver] / hacks / glx / texfont.c
1 /* texfonts, Copyright (c) 2005-2017 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  */
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 #elif defined(HAVE_ANDROID)
30 # include <GLES/gl.h>
31 # include "jwzgles.h"
32 #else
33 # include <GL/glx.h>
34 # include <GL/glu.h>
35 #endif
36
37 #ifdef HAVE_JWZGLES
38 # include "jwzgles.h"
39 #endif /* HAVE_JWZGLES */
40
41 #ifdef HAVE_XSHM_EXTENSION
42 # include "xshm.h"
43 #endif /* HAVE_XSHM_EXTENSION */
44
45 #include "xft.h"
46 #include "pow2.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 /* Given a Pixmap (of screen depth), converts it to an OpenGL luminance mipmap.
86    RGB are averaged to grayscale, and the resulting value is treated as alpha.
87    Pass in the size of the pixmap; the size of the texture is returned
88    (it may be larger, since GL like powers of 2.)
89
90    We use a screen-depth pixmap instead of a 1bpp bitmap so that if the fonts
91    were drawn with antialiasing, that is preserved.
92  */
93 static void
94 bitmap_to_texture (Display *dpy, Pixmap p, Visual *visual, int depth,
95                    int *wP, int *hP)
96 {
97   Bool mipmap_p = True;
98   int ow = *wP;
99   int oh = *hP;
100   GLsizei w2 = (GLsizei) to_pow2 (ow);
101   GLsizei h2 = (GLsizei) to_pow2 (oh);
102   int x, y, max, scale;
103   XImage *image = 0;
104   unsigned char *data = (unsigned char *) calloc (w2 * 2, (h2 + 1));
105   unsigned char *out = data;
106
107   /* OpenGLES doesn't support GL_INTENSITY, so instead of using a
108      texture with 1 byte per pixel, the intensity value, we have
109      to use 2 bytes per pixel: solid white, and an alpha value.
110    */
111 # ifdef HAVE_JWZGLES
112 #  undef GL_INTENSITY
113 # endif
114
115 # ifdef HAVE_XSHM_EXTENSION
116   Bool use_shm = get_boolean_resource (dpy, "useSHM", "Boolean");
117   XShmSegmentInfo shm_info;
118 # endif /* HAVE_XSHM_EXTENSION */
119
120   /* If either dimension is larger than the supported size, reduce.
121      We still return the old size to keep the caller's math working,
122      but the texture itself will have fewer pixels in it.
123    */
124   glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max);
125   scale = 1;
126   while (w2 > max || h2 > max)
127     {
128       w2 /= 2;
129       h2 /= 2;
130       scale *= 2;
131     }
132
133 # ifdef HAVE_XSHM_EXTENSION
134   if (use_shm)
135     {
136       image = create_xshm_image (dpy, visual, depth, ZPixmap, 0, &shm_info,
137                                  ow, oh);
138       if (image)
139         XShmGetImage (dpy, p, image, 0, 0, ~0L);
140       else
141         use_shm = False;
142     }
143 # endif /* HAVE_XSHM_EXTENSION */
144
145   if (!image) {
146     /* XCreateImage fills in (red|green_blue)_mask. XGetImage only does that
147        when reading from a Window, not when it's a Pixmap.
148      */
149     image = XCreateImage (dpy, visual, depth, ZPixmap, 0, NULL, ow, oh,
150                           BitmapPad (dpy), 0);
151     image->data = malloc (image->height * image->bytes_per_line);
152     XGetSubImage (dpy, p, 0, 0, ow, oh, ~0L, ZPixmap, image, 0, 0);
153   }
154
155 # ifdef HAVE_JWZGLES
156   /* This would work, but it's wasteful for no benefit. */
157   mipmap_p = False;
158 # endif
159
160 # ifdef DUMP_BITMAPS
161   fprintf (stderr, "\n");
162 # endif
163   for (y = 0; y < h2; y++) {
164     for (x = 0; x < w2; x++) {
165       /* Might be better to average a scale x scale square of source pixels,
166          but at the resolutions we're dealing with, this is probably good
167          enough. */
168       int sx = x * scale;
169       int sy = y * scale;
170       unsigned long pixel = (sx >= ow || sy >= oh ? 0 :
171                              XGetPixel (image, sx, sy));
172       /* instead of averaging all three channels, let's just use red,
173          and assume it was already grayscale. */
174       unsigned long r = pixel & image->red_mask;
175       /* This goofy trick is to make any of RGBA/ABGR/ARGB work. */
176       pixel = ((r >> 24) | (r >> 16) | (r >> 8) | r) & 0xFF;
177
178 # ifdef DUMP_BITMAPS
179       if (sx < ow && sy < oh)
180 #  ifdef HAVE_JWXYZ
181         fprintf (stderr, "%c", 
182                  r >= 0xFF000000 ? '#' : 
183                  r >= 0x88000000 ? '%' : 
184                  r ? '.' : ' ');
185 #  else
186         fprintf (stderr, "%c", 
187                  r >= 0xFF0000 ? '#' : 
188                  r >= 0x880000 ? '%' : 
189                  r ? '.' : ' ');
190 #  endif
191 # endif
192
193 # if 0  /* Debugging checkerboard */
194       if (sx < ow && sy < oh && (((sx / 4) & 1) ^ ((sy / 4) & 1)))
195         pixel = 0x3F;
196 # endif
197
198 # ifndef GL_INTENSITY
199       *out++ = 0xFF;  /* 2 bytes per pixel (luminance, alpha) */
200 # endif
201       *out++ = pixel;
202     }
203 # ifdef DUMP_BITMAPS
204     fprintf (stderr, "\n");
205 # endif
206   }
207
208 # ifdef HAVE_XSHM_EXTENSION
209   if (use_shm)
210     destroy_xshm_image (dpy, image, &shm_info);
211   else
212 # endif /* HAVE_XSHM_EXTENSION */
213   {
214     /* We malloc'd image->data, so we free it. */
215     free (image->data);
216     image->data = NULL;
217     XDestroyImage (image);
218   }
219
220   image = 0;
221
222   {
223 # ifdef GL_INTENSITY
224     GLuint iformat = GL_INTENSITY;
225     GLuint format  = GL_LUMINANCE;
226 # else
227     GLuint iformat = GL_LUMINANCE_ALPHA;
228     GLuint format  = GL_LUMINANCE_ALPHA;
229 # endif
230     GLuint type    = GL_UNSIGNED_BYTE;
231
232     if (mipmap_p)
233       gluBuild2DMipmaps (GL_TEXTURE_2D, iformat, w2, h2, format, type, data);
234     else
235       glTexImage2D (GL_TEXTURE_2D, 0, iformat, w2, h2, 0, format, type, data);
236   }
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-*-*-180-*-*-*-*-*-*"; /* also fps.c */
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       if (!prev) abort();
489       free (prev->string);
490       prev->string     = 0;
491       prev->tex_width  = 0;
492       prev->tex_height = 0;
493       memset (&prev->extents, 0, sizeof(prev->extents));
494       if (prev2)
495         prev2->next = 0;
496       if (prev != data->cache)
497         prev->next = data->cache;
498       data->cache = prev;
499       return prev;
500     }
501
502   /* Not cached, and cache not full.  Add a new entry at the front,
503      and allocate a new texture for it.
504    */
505   curr = (struct texfont_cache *) calloc (1, sizeof(*prev));
506   glGenTextures (1, &curr->texid);
507   curr->string = 0;
508   curr->next = data->cache;
509   data->cache = curr;
510
511   return curr;
512 }
513
514
515 /* Renders the given string into the prevailing texture.
516    Returns the metrics of the text, and size of the texture.
517  */
518 void
519 string_to_texture (texture_font_data *data, const char *string,
520                    XCharStruct *extents_ret,
521                    int *tex_width_ret, int *tex_height_ret)
522 {
523   Window window = RootWindow (data->dpy, 0);
524   Pixmap p;
525   XGCValues gcv;
526   GC gc;
527   XWindowAttributes xgwa;
528   XRenderColor rcolor;
529   XftColor xftcolor;
530   XftDraw *xftdraw;
531   int width, height;
532   XCharStruct overall;
533
534   /* Measure the string and create a Pixmap of the proper size.
535    */
536   XGetWindowAttributes (data->dpy, window, &xgwa);
537   iterate_texture_string (data, string, 0, 0, 0, 0, &overall);
538   width  = overall.rbearing - overall.lbearing;
539   height = overall.ascent   + overall.descent;
540   if (width  <= 0) width  = 1;
541   if (height <= 0) height = 1;
542   p = XCreatePixmap (data->dpy, window, width, height, xgwa.depth);
543
544   gcv.foreground = BlackPixelOfScreen (xgwa.screen);
545   gc = XCreateGC (data->dpy, p, GCForeground, &gcv);
546   XFillRectangle (data->dpy, p, gc, 0, 0, width, height);
547   XFreeGC (data->dpy, gc);
548
549   /* Render the string into the pixmap.
550    */
551   rcolor.red = rcolor.green = rcolor.blue = rcolor.alpha = 0xFFFF;
552   XftColorAllocValue (data->dpy, xgwa.visual, xgwa.colormap,
553                       &rcolor, &xftcolor);
554   xftdraw = XftDrawCreate (data->dpy, p, xgwa.visual, xgwa.colormap);
555   iterate_texture_string (data, string,
556                           -overall.lbearing, overall.ascent,
557                           xftdraw, &xftcolor, 0);
558   XftDrawDestroy (xftdraw);
559   XftColorFree (data->dpy, xgwa.visual, xgwa.colormap, &xftcolor);
560
561   /* Copy the bits from the Pixmap into a texture, unless it's cached.
562    */
563   bitmap_to_texture (data->dpy, p, xgwa.visual, xgwa.depth, 
564                      &width, &height);
565   XFreePixmap (data->dpy, p);
566
567   if (extents_ret)    *extents_ret    = overall;
568   if (tex_width_ret)  *tex_width_ret  = width;
569   if (tex_height_ret) *tex_height_ret = height;
570 }
571
572
573 /* Set the various OpenGL parameters for properly rendering things
574    with a texture generated by string_to_texture().
575  */
576 void
577 enable_texture_string_parameters (void)
578 {
579   glEnable (GL_TEXTURE_2D);
580
581   /* Texture-rendering parameters to make font pixmaps tolerable to look at.
582    */
583   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
584   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
585                    GL_LINEAR_MIPMAP_LINEAR);
586
587   /* LOD bias is part of OpenGL 1.4.
588      GL_EXT_texture_lod_bias has been present since the original iPhone.
589   */
590 # if !defined(GL_TEXTURE_LOD_BIAS) && defined(GL_TEXTURE_LOD_BIAS_EXT)
591 #   define GL_TEXTURE_LOD_BIAS GL_TEXTURE_LOD_BIAS_EXT
592 # endif
593 # ifdef GL_TEXTURE_LOD_BIAS
594   glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.25);
595 # endif
596   clear_gl_error();  /* invalid enum on iPad 3 */
597
598   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
599   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
600
601   /* Don't write the transparent parts of the quad into the depth buffer. */
602   glAlphaFunc (GL_GREATER, 0.01);
603   glEnable (GL_ALPHA_TEST);
604   glEnable (GL_BLEND);
605   glDisable (GL_LIGHTING);
606   glDisable (GL_TEXTURE_GEN_S);
607   glDisable (GL_TEXTURE_GEN_T);
608   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
609 }
610
611
612 /* Draws the string in the scene at the origin.
613    Newlines and tab stops are honored.
614    Any numbers inside [] will be rendered as a subscript.
615    Assumes the font has been loaded as with load_texture_font().
616
617    The origin is at the origin of the first character, so subsequent
618    lines of a multi-line string are below that.
619  */
620 void
621 print_texture_string (texture_font_data *data, const char *string)
622 {
623   XCharStruct overall;
624   int tex_width, tex_height;
625   texfont_cache *cache;
626   GLint old_texture;
627
628   if (!*string) return;
629
630   clear_gl_error ();
631
632   /* Save the prevailing texture ID, and bind ours.  Restored at the end. */
633   glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture);
634
635   cache = get_cache (data, string);
636
637   glBindTexture (GL_TEXTURE_2D, cache->texid);
638   check_gl_error ("texture font binding");
639
640   /* Measure the string and make a pixmap that will fit it,
641      unless it's cached.
642    */
643   if (cache->string)
644     {
645       overall    = data->cache->extents;
646       tex_width  = data->cache->tex_width;
647       tex_height = data->cache->tex_height;
648     }
649   else
650     {
651 # if defined HAVE_JWXYZ && defined JWXYZ_GL
652       /* TODO, JWXYZ_GL: Mixing Xlib and GL has issues. */
653       memset (&overall, 0, sizeof(overall));
654       tex_width = 8;
655       tex_height = 8;
656 # else
657       string_to_texture (data, string, &overall, &tex_width, &tex_height);
658 # endif
659     }
660
661   {
662     int ofront, oblend;
663     Bool alpha_p, blend_p, light_p, gen_s_p, gen_t_p;
664     GLfloat omatrix[16];
665     GLfloat qx0, qy0, qx1, qy1;
666     GLfloat tx0, ty0, tx1, ty1;
667
668     /* If face culling is not enabled, draw front and back. */
669     Bool draw_back_face_p = !glIsEnabled (GL_CULL_FACE);
670
671     /* Save the prevailing texture environment, and set up ours.
672      */
673     glGetIntegerv (GL_FRONT_FACE, &ofront);
674     glGetIntegerv (GL_BLEND_DST, &oblend);
675     glGetFloatv (GL_TEXTURE_MATRIX, omatrix);
676     blend_p = glIsEnabled (GL_BLEND);
677     alpha_p = glIsEnabled (GL_ALPHA_TEST);
678     light_p = glIsEnabled (GL_LIGHTING);
679     gen_s_p = glIsEnabled (GL_TEXTURE_GEN_S);
680     gen_t_p = glIsEnabled (GL_TEXTURE_GEN_T);
681
682     glPushMatrix();
683
684     glNormal3f (0, 0, 1);
685     glFrontFace (GL_CW);
686
687     glMatrixMode (GL_TEXTURE);
688     glLoadIdentity ();
689     glMatrixMode (GL_MODELVIEW);
690
691     enable_texture_string_parameters();
692
693     /* Draw a quad with that texture on it, possibly using a cached texture.
694        Position the XCharStruct origin at 0,0 in the scene.
695      */
696     qx0 =  overall.lbearing;
697     qy0 = -overall.descent;
698     qx1 =  overall.rbearing;
699     qy1 =  overall.ascent;
700
701     tx0 = 0;
702     ty1 = 0;
703     tx1 = (overall.rbearing - overall.lbearing) / (GLfloat) tex_width;
704     ty0 = (overall.ascent + overall.descent)    / (GLfloat) tex_height;
705
706     glEnable (GL_CULL_FACE);
707     glFrontFace (GL_CCW);
708     glBegin (GL_QUADS);
709     glTexCoord2f (tx0, ty0); glVertex3f (qx0, qy0, 0);
710     glTexCoord2f (tx1, ty0); glVertex3f (qx1, qy0, 0);
711     glTexCoord2f (tx1, ty1); glVertex3f (qx1, qy1, 0);
712     glTexCoord2f (tx0, ty1); glVertex3f (qx0, qy1, 0);
713     glEnd();
714
715     if (draw_back_face_p)
716       {
717         glFrontFace (GL_CW);
718         glBegin (GL_QUADS);
719         glTexCoord2f (tx0, ty0); glVertex3f (qx0, qy0, 0);
720         glTexCoord2f (tx1, ty0); glVertex3f (qx1, qy0, 0);
721         glTexCoord2f (tx1, ty1); glVertex3f (qx1, qy1, 0);
722         glTexCoord2f (tx0, ty1); glVertex3f (qx0, qy1, 0);
723         glEnd();
724         glDisable (GL_CULL_FACE);
725       }
726
727     glPopMatrix();
728
729     /* Reset to the caller's texture environment.
730      */
731     glBindTexture (GL_TEXTURE_2D, old_texture);
732     glFrontFace (ofront);
733     if (!alpha_p) glDisable (GL_ALPHA_TEST);
734     if (!blend_p) glDisable (GL_BLEND);
735     if (light_p) glEnable (GL_LIGHTING);
736     if (gen_s_p) glEnable (GL_TEXTURE_GEN_S);
737     if (gen_t_p) glEnable (GL_TEXTURE_GEN_T);
738
739     glBlendFunc (GL_SRC_ALPHA, oblend);
740   
741     glMatrixMode (GL_TEXTURE);
742     glMultMatrixf (omatrix);
743     glMatrixMode (GL_MODELVIEW);
744
745     check_gl_error ("texture font print");
746
747     /* Store this string into the cache, unless that's where it came from.
748      */
749     if (!cache->string)
750       {
751         cache->string     = strdup (string);
752         cache->extents    = overall;
753         cache->tex_width  = tex_width;
754         cache->tex_height = tex_height;
755       }
756   }
757 }
758
759
760 /* Draws the string on the window at the given pixel position.
761    Newlines and tab stops are honored.
762    Any numbers inside [] will be rendered as a subscript.
763    Assumes the font has been loaded as with load_texture_font().
764
765    Position is 0 for center, 1 for top left, 2 for bottom left.
766  */
767 void
768 print_texture_label (Display *dpy,
769                      texture_font_data *data,
770                      int window_width, int window_height,
771                      int position,
772                      const char *string)
773 {
774   GLfloat color[4];
775
776   Bool tex_p   = glIsEnabled (GL_TEXTURE_2D);
777   Bool texs_p  = glIsEnabled (GL_TEXTURE_GEN_S);
778   Bool text_p  = glIsEnabled (GL_TEXTURE_GEN_T);
779   Bool depth_p = glIsEnabled (GL_DEPTH_TEST);
780   Bool cull_p  = glIsEnabled (GL_CULL_FACE);
781   Bool fog_p   = glIsEnabled (GL_FOG);
782   GLint ovp[4];
783
784 #  ifndef HAVE_JWZGLES
785   GLint opoly[2];
786   glGetIntegerv (GL_POLYGON_MODE, opoly);
787 #  endif
788
789   glGetIntegerv (GL_VIEWPORT, ovp);
790
791   glGetFloatv (GL_CURRENT_COLOR, color);
792
793   glEnable (GL_TEXTURE_2D);
794   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
795   glPolygonMode (GL_FRONT, GL_FILL);
796
797   glDisable (GL_TEXTURE_GEN_S);
798   glDisable (GL_TEXTURE_GEN_T);
799   glDisable (GL_CULL_FACE);
800   glDisable (GL_FOG);
801
802   glDisable (GL_DEPTH_TEST);
803
804   /* Each matrix mode has its own stack, so we need to push/pop
805      them separately.
806    */
807   glMatrixMode(GL_PROJECTION);
808   glPushMatrix();
809   {
810     glLoadIdentity();
811
812     glMatrixMode(GL_MODELVIEW);
813     glPushMatrix();
814     {
815       XCharStruct cs;
816       int ascent, descent;
817       int x, y, w, h, swap;
818       /* int rot = (int) current_device_rotation(); */
819       int rot = 0;  /* Since GL hacks rotate now */
820
821       glLoadIdentity();
822       glViewport (0, 0, window_width, window_height);
823       glOrtho (0, window_width, 0, window_height, -1, 1);
824
825       while (rot <= -180) rot += 360;
826       while (rot >   180) rot -= 360;
827
828       texture_string_metrics (data, string, &cs, &ascent, &descent);
829       h = cs.ascent + cs.descent;
830       w  = cs.width;
831
832 # ifdef USE_IPHONE
833       {
834         /* Size of the font is in points, so scale iOS pixels to points. */
835         GLfloat scale = ((window_width > window_height
836                           ? window_width : window_height)
837                          / 768.0);
838         if (scale < 1) scale = 1;
839
840         /* jwxyz-XLoadFont has already doubled the font size, to compensate
841            for physically smaller screens.  Undo that, since OpenGL hacks
842            use full-resolution framebuffers, unlike X11 hacks. */
843         scale /= 2;
844
845         window_width  /= scale;
846         window_height /= scale;
847         glScalef (scale, scale, scale);
848       }
849 # endif /* USE_IPHONE */
850
851       if (rot > 135 || rot < -135)              /* 180 */
852         {
853           glTranslatef (window_width, window_height, 0);
854           glRotatef (180, 0, 0, 1);
855         }
856       else if (rot > 45)                        /* 90 */
857         {
858           glTranslatef (window_width, 0, 0);
859           glRotatef (90, 0, 0, 1);
860           swap = window_width;
861           window_width = window_height;
862           window_height = swap;
863         }
864       else if (rot < -45)                       /* 270 */
865         {
866           glTranslatef(0, window_height, 0);
867           glRotatef (-90, 0, 0, 1);
868           swap = window_width;
869           window_width = window_height;
870           window_height = swap;
871         }
872
873       switch (position) {
874       case 0:                                   /* center */
875         x = (window_width  - w) / 2;
876         y = (window_height + h) / 2 - ascent;
877         break;
878       case 1:                                   /* top */
879         x = ascent;
880         y = window_height - ascent*2;
881         break;
882       case 2:                                   /* bottom */
883         x = ascent;
884         y = h;
885         break;
886       default:
887         abort();
888       }
889
890       glTranslatef (x, y, 0);
891
892       /* draw the text five times, to give it a border. */
893       {
894         const XPoint offsets[] = {{ -1, -1 },
895                                   { -1,  1 },
896                                   {  1,  1 },
897                                   {  1, -1 },
898                                   {  0,  0 }};
899         int i;
900
901         glColor3f (0, 0, 0);
902         for (i = 0; i < countof(offsets); i++)
903           {
904             if (offsets[i].x == 0)
905               glColor4fv (color);
906             glPushMatrix();
907             glTranslatef (offsets[i].x, offsets[i].y, 0);
908             print_texture_string (data, string);
909             glPopMatrix();
910           }
911       }
912     }
913     glPopMatrix();
914   }
915   glMatrixMode(GL_PROJECTION);
916   glPopMatrix();
917
918   if (tex_p)   glEnable (GL_TEXTURE_2D); else glDisable (GL_TEXTURE_2D);
919   if (texs_p)  glEnable (GL_TEXTURE_GEN_S);/*else glDisable(GL_TEXTURE_GEN_S);*/
920   if (text_p)  glEnable (GL_TEXTURE_GEN_T);/*else glDisable(GL_TEXTURE_GEN_T);*/
921   if (depth_p) glEnable (GL_DEPTH_TEST); else glDisable (GL_DEPTH_TEST);
922   if (cull_p)  glEnable (GL_CULL_FACE); /*else glDisable (GL_CULL_FACE);*/
923   if (fog_p)   glEnable (GL_FOG); /*else glDisable (GL_FOG);*/
924
925   glViewport (ovp[0], ovp[1], ovp[2], ovp[3]);
926
927 # ifndef HAVE_JWZGLES
928   glPolygonMode (GL_FRONT, opoly[0]);
929 # endif
930
931   glMatrixMode(GL_MODELVIEW);
932 }
933
934
935 #ifdef HAVE_JWXYZ
936 char *
937 texfont_unicode_character_name (texture_font_data *data, unsigned long uc)
938 {
939   Font fid = data->xftfont->xfont->fid;
940   return jwxyz_unicode_character_name (data->dpy, fid, uc);
941 }
942 #endif /* HAVE_JWXYZ */
943
944
945
946 /* Releases the font and texture.
947  */
948 void
949 free_texture_font (texture_font_data *data)
950 {
951   while (data->cache)
952     {
953       texfont_cache *next = data->cache->next;
954       glDeleteTextures (1, &data->cache->texid);
955       free (data->cache);
956       data->cache = next;
957     }
958   if (data->xftfont)
959     XftFontClose (data->dpy, data->xftfont);
960   free (data);
961 }